1. 程式人生 > >【NOIP2017模擬賽】二分圖+狀態壓縮DP Graph(好題)

【NOIP2017模擬賽】二分圖+狀態壓縮DP Graph(好題)

這裡寫圖片描述

題解

這道題其實是一個NP完全問題(23333),但是由於資料小啊,我們可以搞一搞。很容易發現,如果我們將一個點拆成兩個點,一個代表出點,一個代表入點。當增加了一條有向邊,就出點向入點連一條邊(例如將u拆成u1u2v拆成v1v2,然後邊u>v就變成邊u1>v2),我們發現這樣就變成了一個二分圖(怎麼可能兩個出點或兩個入點之間有一條邊呢……),此時二分圖的任意一個完備匹配,都是符合題意的環組。也就是說,我們只需要求二分圖完備匹配的方案數即可。
此時我們考慮狀態壓縮DP,f[i][s]s為一個被壓縮的狀態)表示前i個出點匹配了狀態為s的入點的方案數(前提是|s|

=i),可以很容易得到狀態轉移方程,於是我們就做出來了……依然是沒有註釋的程式碼……將就能看就行……

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 25
#define mod 998244353
#define P 1048580
int n,m,lg2[P],bit[P];
bool A[N][N];
int f[N][P];
int getint()
{
    int p=0;
    char c=getchar();
    while
(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')p=p*10+c-'0',c=getchar(); return p; } int main() { n=getint();m=getint(); lg2[1]=1;bit[1]=1; for(int i=2;i<=(1<<20);i++) { lg2[i]=lg2[i>>1]+1; for(int j=0;j<=20;j++) if(i&(1
<<j)) bit[i]++; } for(int i=1;i<=m;i++) { int a=getint(),b=getint(); A[a][b]=1; } f[0][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<(1<<n);j++) { if(bit[j]!=i) continue; for(int k=j;k;k-=k&-k) { int v=lg2[k&-k]; if(A[i][v]) f[i][j]=(f[i][j]+f[i-1][j-(k&-k)])%mod; } } } printf("%d\n",f[n][(1<<n)-1]); }