zoukankan      html  css  js  c++  java
  • 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
Copyright © 2011-2022 走看看