思路不難想,細節比較多。
思路
觀察到 \(n \le 19\) ,首先想到狀壓 dp 。
於是自然地定義 \(dp[j][i]\) 為:抵達點的狀態為 \(i\) ,且此時在點 \(j\) 時,簡單路徑的條數。注意這裡是簡單路徑的條數,而不是環的個數,因為環的個數要在 dp 過程中統計。這裡的 dp 作用就在於求簡單路徑條數。
在轉移的時候,我們先定下當前狀態 \(i\) ,然後選定當前處於哪個點 \(j\) ,最後決定去到哪個點 \(k\) 。與一般的吃乳酪模型不太一樣,\(k\) 可以不在 \(i\) 中。
對於 \(k\) 在 \(i\) 中且 \(k\) 為起點的情況,此時重複抵達一個點,這時候我們找到了環,則 \(ans+=dp[j][i]\) 。
對於 \(k\) 不在 \(i\) 中的情況,就要去到 \(k\) 那裡,則 \(dp[k][i|(1<<k)]+=dp[j][i]\) 。
因為起點不同,經過的邊相同的環視為同一個環,所以我們假定起點為當前狀態的 lowbit ,注意在列舉 \(k\) 時我們不能讓 \((1<<k)<lowbit(i)\) ,因為如果這樣那麼我們的起點就改變了,會統計到重複的。
另外,因為我們會把所有的無向邊統計進去,所以我們的 \(ans-m\) 。又因為一條環會正著走一遍,反著走一遍,所以要把 \((ans-m)/2\) ,即為最終結果。
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
bool g[55][55];
ll dp[20][600000],ans=0;
int lowbit(int x){return x&-x;}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
u--,v--;
g[u][v]=g[v][u]=1;
}
for(int i=0;i<n;i++)dp[i][1<<i]=1;
for(int i=0;i<(1<<n);i++)
{
for(int j=0;j<n;j++)
{
if(((i>>j)&1)==0)continue;
for(int k=0;k<n;k++)
{
if(!g[j][k]||j==k)continue;
if(lowbit(i)>(1<<k))continue;// 一定要加特判,防止起點變化
if((i>>k)&1)
{
if(lowbit(i)==(1<<k))ans+=dp[j][i];
}
else dp[k][i|(1<<k)]+=dp[j][i];
}
}
}
cout<<(ans-m)/2;
return 0;
}