題目描述
給定一個nn個點的無向圖,求這個圖中有多少條長度為
4
的簡單路徑。
n≤
1500
輸入
第一行一個數n
接下來n行每行n個
0
或
1
第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
個二進制位要存儲
所以我們再加一維放入
50
個
longint
(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.