Codeforces 11D A Simple Task 題解 [ 藍 ] [ 狀壓 dp ]

KS_Fszha發表於2024-07-31

思路不難想,細節比較多。

思路

觀察到 \(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;
}

相關文章