題目描述
給定一個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.

