簡單路徑


題目描述

給定一個nn個點的無向圖,求這個圖中有多少條長度為4的簡單路徑。

n≤1500

  

輸入

第一行一個數n

接下來n行每行n個01

第i行第j列是1表示i與j聯通

輸出

輸出簡單路徑的個數

  

樣例輸入

5

00011

00000

00010

10100

10000

樣例輸出

2

提示

n<=1500

  

這道題目的朴素算法可以直接想到—枚舉圖中的4個點,判斷是否構成一條簡單路徑,時間復雜度O(n^4

  

那么必然要進行優化

  

如何優化呢?

  

我們枚舉每一條邊,統計連向該邊左右兩端的節點

  

  

  

如圖所示

 

由乘法原理可得,簡單路徑的數量為n*m

同時我們不能忽略環的存在,所以還要減去環的數量

所以ans=ans+(節點u的入度-1)*(節點v的入讀-1)-三元環的數量{注意本身枚舉的那一條邊}

此時時間復雜度為O(n^3),似乎還是A不了誒

此時我們把目光放在了尋找三元環的n上

怎么才能將這個n降下來呢

嗯.......壓位!!!

對於每個節點i,我們用f[i]數組來表示它的連接情況

其中里面所存放的是一個二進制數

數中的第k位表示的是點i是否與點k相連

我們發現n<=1500,那么就有1500個二進制位要存儲

所以我們再加一維放入50longint(c++是int 32位)

那么處理三元環就直接變成了n/30(對於f數組我們進行預處理)

時間復雜度就變成了(n^3/30

n<=1500這個時間復雜度是可以接受的

接下來是代碼

  

var

n,i,j,k,t,l,dd:longint; 

ans,sum:int64; 

f:array[1..3000,1..100] of longint; 

a:array[1..1600,1..1600] of char; 

GGG:array[0..1 shl 16] of longint; 

u,v:array[1..1500*1500] of longint; 

s:array[1..2000] of longint; 

function find(q:longint):longint; //對於一個二進制數求解里面有幾個1

var left,right:longint; 

begin

     left:=q shr 15;  right:=q-left shl 15;//因為這個二進制數是30位的,而我們的表只有15位,那么就把這個數掰開 

     exit(GGG[left]+GGG[right]); 

end; 

procedure GG; //打表處理出2~2^16中所有二進制數中的1的數量

begin

     for i:=2 to 1 shl 16  do

     begin

         GGG[i]:=GGG[i div 2]+i mod 2; //遞推求解不解釋

     end;

end; 

begin

     readln(n); 

     GGG[1]:=1; //GGG[i]表示i的二進制中有幾個一

     GG; 

     for i:=1 to n do

     begin

         for j:=1 to n do

         begin

             read(a[i,j]); 

             if (a[i,j]='1') and (i<>j) then

             begin

                 inc(l); 

                 u[l]:=i;  

                 v[l]:=j; //記錄邊兩端的點

                 inc(s[i]);//記錄入度

             end; 

         end; 

         readln; 

     end; 

     for i:=1 to n do

     begin

         for j:=1 to n do

         begin

             t:=j div 30+1; 

             if a[i,j]='1' then f[i][t]:=f[i][t]+(1 shl (j-(t-1)*30+1)); 

         end; 

     end; //構建f數組

     for k:=1 to l do

     begin

         sum:=(s[u[k]]-1)*(s[v[k]]-1); 

         for i:=1 to n div 30+1 do

         begin

             dd:=0; 

             dd:=find(f[v[k]][i] and f[u[k]][i]); //and后二進制位上就直接保留了相同的點

             sum:=sum-dd; 

         end; 

         ans:=ans+sum; 

     end; 

     writeln(ans); 

end.

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM