[bzoj3590]Quare——状压DP

题目大意:

给定一个图,求一个边的子集,使得整张图为边双连通并且边的权值和最小。

思路:

数据范围这么小,考虑状压DP。
题目要求子图为边双连通,边双连通可以表示成若干个环套在一起,但是这样并不方便我们表示状态。
思考一下,不难发现一个边双可以这样组成:一个边双不断地添加一条链并且使这条链首尾都和边双里的任意一个点相连。
于是转移的大致思路便出来了,枚举一条链并且将这一条链接入目前的集合中,每一次保证最小代价。
于是我们要预处理任意一条链自己本身连通的最小值
还有一个点向一个集合中连边的最小值
由于一条链可能只有一个点,所以还要处理次小值
然后直接转移就好了。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<endl
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj3590.in","r",stdin);
	freopen("bzoj3590.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=13;
const int maxm=45;
const int maxw=(1<<13)+10;
const int inf=0x3f3f3f3f;
int T,n,m,dp[maxw],all;
int G[maxn][maxn],line[maxn][maxn][maxw],mx[maxn][maxw][2];
vector<pii>to[maxn];

void init(){
	read(n); read(m);
	all=(1<<n)-1;
	REP(i,1,m){
		int u,v,w;
		read(u),read(v),read(w);
		G[u][v]=min(G[u][v],w);
		G[v][u]=min(G[v][u],w);
		to[u].pb(mk(v,w));
		to[v].pb(mk(u,w));
	}
	REP(i,1,n)line[i][i][1<<(i-1)]=0;
	REP(S,1,all){
		if(__builtin_popcount(S)==1)continue;
		REP(L,1,n)if((1<<(L-1))&S)
			REP(R,1,n)if(((1<<(R-1))&S) && L!=R){
				REP(k,1,n)if(((1<<(k-1))&S) && k!=L){
					int va=G[L][k],vb=line[k][R][S^(1<<(L-1))];
					if(va==inf || vb==inf)continue;
					line[L][R][S]=min(line[L][R][S],va+vb);
				}
			}
	}
	REP(i,1,n)REP(S,1,all)if(!((1<<(i-1))&S)){
		REP(j,0,to[i].size()-1){
			int v=to[i][j].fi,val=to[i][j].se;
			if(!((1<<(v-1))&S))continue;
			if(val<mx[i][S][0])swap(mx[i][S][0],mx[i][S][1]),mx[i][S][0]=val;
			else mx[i][S][1]=min(mx[i][S][1],val);
		}
	}
}

void work(){
	dp[1]=0;
	REP(S,0,all){
		if(dp[S]==inf)continue;
		for(int S0=(all^S);S0;S0=(S0-1)&(all^S)){
			int va,vb,vc;
			REP(i,1,n)if((1<<(i-1))&S0){
				if(__builtin_popcount(S0)==1){
					va=mx[i][S][0],vb=mx[i][S][1];
					if(va!=inf && vb!=inf)
						dp[S^S0]=min(dp[S^S0],dp[S]+va+vb);
					break;
				}
				REP(j,1,n)if(((1<<(j-1))&S0) && i!=j){
					va=mx[i][S][0],vb=mx[j][S][0],vc=line[i][j][S0];
					if(va!=inf && vb!=inf && vc!=inf)
						dp[S^S0]=min(dp[S^S0],dp[S]+va+vb+vc);
				}
			}
		}
	}
	if(dp[all]!=inf)printf("%d\n",dp[all]);
	else puts("impossible");
}

int main(){
	File();
	read(T);
	while(T--){
		memset(line,63,sizeof(line));
		memset(mx,63,sizeof(mx));
		memset(G,63,sizeof(G));
		memset(dp,63,sizeof(dp));
		init();
		work();
		REP(i,0,maxn-1)to[i].clear();
	}
	return 0;
}

posted @ 2018-10-27 14:38  ylsoi  阅读(131)  评论(0编辑  收藏  举报