P6085-[JSOI2013]吃货JYY【状压dp,欧拉回路】

正题

题目链接:https://www.luogu.com.cn/problem/P6085


题目大意

(n)个点的一张无向图,有(k)条必走边,(m)条其他边,求从(1)出发经过必走边后回到起点的最短路径。

(2leq nleq 13,0leq kleq 78,2leq mleq 200)


解题思路

可以理解为在只包含必走边的图上加若干条其他边使得这张图存在欧拉回路。

欧拉回路要求所有点联通且度数为偶数,考虑状态压缩(dp),设三进制的状态。

(f_s)(0)表示没有联通,(1)表示度数为奇数,(2)表示度数为偶数。

然后先考虑加点进来的方式,也就是加进来的点我们只考虑不是必须的边的部分。而且使用这些点类似于一棵树的连接联通的点。(并不是连接成真正的树,而是如果使用了不必须的边的话只和一个点联通)

然后处理完后再考虑调整图的奇偶性,设(g_S)表示集合(S)中的点为奇数时调整为偶数的最小代价。

然后用(f)(g)计算答案就好了。

时间复杂度(O(3^nn^2))


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=14;
struct node{
	int to,next;
}a[N*N];
int n,k,m,tot,ans,sta,st,ls[N],p[N],deg[N];
int dis[N][N],g[1<<N],f[1594323];
queue<int> q;
void addl(int x,int y){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;return;
}
int main()
{
	memset(dis,0x3f,sizeof(dis));
	memset(g,0x3f,sizeof(g));
	memset(f,0x3f,sizeof(f));
	scanf("%d%d",&n,&k);p[0]=1;dis[0][0]=0;
	for(int i=1;i<=n;i++)p[i]=p[i-1]*3,dis[i][i]=0;
	for(int i=1;i<=k;i++){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);x--;y--;
		addl(x,y);addl(y,x);dis[x][y]=dis[y][x]=min(dis[x][y],w);
		deg[x]++;deg[y]++;sta^=(1<<x)^(1<<y);ans+=w;
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);x--;y--;
		dis[x][y]=dis[y][x]=min(dis[x][y],w);
	}
	for(int k=0;k<n;k++)
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	int MS=(1<<n);g[0]=0;
	for(int s=0;s<MS;s++)
		for(int i=0;i<n;i++){
			if((s>>i)&1)continue;
			for(int j=i+1;j<n;j++)
				if(!((s>>j)&1)){
					int z=s^(1<<i)^(1<<j);
					g[s^z]=min(g[s^z],g[s]+dis[i][j]);
				}
		}
	q.push(2);f[2]=0;
	while(!q.empty()){
		int s=q.front();q.pop();
		for(int x=0;x<n;x++){
			if(s/p[x]%3)continue;
			int t=s+p[x]*2;
			for(int i=ls[x];i;i=a[i].next){
				int y=a[i].to;
				if(!(s/p[y]%3))continue;
				if(f[t]>=g[MS])q.push(t);
				f[t]=min(f[t],f[s]);
			}
			for(int y=0;y<n;y++){
				if(!(s/p[y]%3))continue;
				t=s+p[x];
				if((t/p[y]%3)==2)t-=p[y];
				else t+=p[y];
				if(f[t]>=g[MS])q.push(t);
				f[t]=min(f[t],f[s]+dis[x][y]);
			}
		}
	}
	int mins=g[MS];
	for(int s=0;s<p[n];s++){
		bool flag=0;int st=0;
		for(int i=0;i<n;i++){
			if((s/p[i]%3)==0&&deg[i]){flag=1;break;}
			if(s/p[i]%3)st|=(1<<i)*(2-s/p[i]%3);
		}
		if(flag)continue;st^=sta;
		mins=min(mins,f[s]+g[st]);
	}
	printf("%d
",ans+mins);
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15024112.html