CF11D A Simple Task(狀壓DP)

$ solution: $

思路你們應該都懂:ios

狀壓DP: $ f[i][j] $ ,其中 $ i $ 這一維是須要狀壓的,用來記錄19個節點每個是否已經走過(走過爲 $ 1 $ ,沒走爲 $ 0 $ ,用 $ 2 $ 進制 壓縮一下便可)。同時,咱們認爲狀壓中已經走過的序號最小的節點爲出發節點, $ j $ 即數組第二維是路徑終點。(當這兩個數相同時,說明找到了一個環)。數組

注:這種方法由於無向圖的存在,會出現(同一條路徑出現兩次)(一條邊和兩個端點構成非法環)的狀況,這隻須要在輸出答案時 $ ans=(ans-m)/2 $ 便可!spa

$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int

using namespace std;

int n,m,t,u,v;
bool a[19][19]; //存邊
ll ans,f[600001][19]; //狀壓

inline int qr(){ //快讀
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	int res=ch^48;
	while((ch=getchar())>='0'&&ch<='9')
		res=res*10+(ch^48);
	return res;
}

int main(){
	//freopen("hamilton.in","r",stdin);
	//freopen("hamilton.out","w",stdout);
	n=qr(),m=qr();t=1<<n;
	for(rg i=1;i<=m;++i){
		u=qr()-1;v=qr()-1;
		a[u][v]=a[v][u]=1;//加邊
	}
	for(rg i=0;i<n;++i)
		f[1<<i][i]=1; //初始化(建立以i爲起點的路徑)
	for(rg i=1;i<=t;++i){
		for(rg j=0;j<n;++j){
			if(!f[i][j])continue; //加速
			for(rg k=0;k<n;++k){
				if(!a[j][k])continue; //加速
				if((i&-i)>1<<k)continue; //起點不能改!!!(去重)
				if(1<<k&i){ //這個點走過
					if(1<<k==(i&-i)) //起點與終點相同
						ans+=f[i][j];
				}else f[i|1<<k][k]+=f[i][j]; //沒走過就走!
			}
		}
	}printf("%lld",(ans-m)/2); //處理以後再輸出!
	return 0;
}
相關文章
相關標籤/搜索