正题
题目链接:https://www.luogu.com.cn/problem/P6085
题目大意
nnn个点的一张无向图,有kkk条必走边,mmm条其他边,求从111出发经过必走边后回到起点的最短路径。
2≤n≤13,0≤k≤78,2≤m≤2002\leq n\leq 13,0\leq k\leq 78,2\leq m\leq 2002≤n≤13,0≤k≤78,2≤m≤200
解题思路
可以理解为在只包含必走边的图上加若干条其他边使得这张图存在欧拉回路。
欧拉回路要求所有点联通且度数为偶数,考虑状态压缩dpdpdp,设三进制的状态。
fsf_sfs,000表示没有联通,111表示度数为奇数,222表示度数为偶数。
然后先考虑加点进来的方式,也就是加进来的点我们只考虑不是必须的边的部分。而且使用这些点类似于一棵树的连接联通的点。(并不是连接成真正的树,而是如果使用了不必须的边的话只和一个点联通)
然后处理完后再考虑调整图的奇偶性,设gSg_SgS表示集合SSS中的点为奇数时调整为偶数的最小代价。
然后用fff和ggg计算答案就好了。
时间复杂度O(3nn2)O(3^nn^2)O(3nn2)
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[z]=min(g[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&°[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\n",ans+mins);return 0;
}