[欧拉回路][状压dp] Jzoj P3290 吃货JYY

Description

世界上一共有N个JYY愿意去的城市,分别从1编号到N。JYY选出了K个他一定要乘坐的航班。除此之外,还有M个JYY没有特别的偏好,可以乘坐也可以不乘坐的航班。
一个航班我们用一个三元组(x,y,z)来表示,意义是这趟航班连接城市x和y,并且机票费用是z。每个航班都是往返的,所以JYY花费z的钱,既可以选择从x飞往y,也可以选择从y飞往x。
南京的编号是1,现在JYY打算从南京出发,乘坐所有K个航班,并且最后回到南京,请你帮他求出最小的花费。
 

Input

输入数据的第一行包含两个整数N和K;
接下来K行,每行三个整数x,y,z描述必须乘坐的航班的信息,数据保证在这K个航班中,不会有两个不同的航班在同一对城市之间执飞;
第K+2行包含一个整数M;
接下来M行,每行三个整数x,y,z 描述可以乘坐也可以不乘坐的航班信息。

Output

输出一行一个整数,表示最少的花费。数据保证一定存在满足JYY要求的旅行方案。
 

Sample Input

6 3
1 2 1000
2 3 1000
4 5 500
2
1 4 300
3 5 300

Sample Output

3100
 

Data Constraint

对于10%的数据满足N≤4;
对于30%的数据满足N≤ 7;
对于额外30%的数据满足,JYY可以只通过必须乘坐的K个航班从南京出发到达任意一个城市;
对于100%的数据满足2≤N≤13,0≤K≤78,2 ≤M ≤ 200,1 ≤x,y ≤N,1 ≤z ≤ 10^4。
 

Hint

样例说明:一个可行的最佳方案为123541。 机票所需的费用为1000+1000+300+500+300=3100。

题解

  • 题目大意:给出k条必须走的路线,和m条选择走的路线,求一条经过k条必须走路径从1出发又回到1的路径(每条路都是双向的,只用给一份钱)
  • 对于答案就是连通图里的一个子图,这个子图保证是欧拉图
  • 首先,我们可以用Floyd求出任意两点的最短距离
  • 然后可以用f[s],求出当前跑了的点的状态为s的最短距离
  • 因为最后答案绝对是一个连通图
  • 那么,可以把1先打进连通图
  • 然后不断往连通图里添加点和边
  • 对于一个欧拉图,任意一点的度都为偶数,不然不满足欧拉图的性质(有进无出)
  • 那么可以用三进制压缩状态,0表示与连通图不相连,1表示度数为奇数,2表示度数为偶数
  • 那么对于往连通图里加边,有两种情况:
  • ①i与一条必须走的路径与连通图相连
  • 因为k必须被经过一次,那么k不产生额外代价
  • ②i与连通图里的一个点j直接相连,它们之间的距离就是两点之见的距离(预处理得出)
  • 以上加点加边都要改变它们的奇偶性,也就是2——1,1——2
  • 如果这时,求出了连通图的最小代价,其实不一定满足每一个点的度数都为奇数
  • 那么可以将它们两两配对
  • 将其最短距离加入图中
  • 最后还要加上k条边的代价
  • 就是answer

代码

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<queue>
  6 using namespace std;
  7 const int inf=0x3f3f3f3f;
  8 queue<int>Q;
  9 int n,m,dis[16][16],cnt,head[16],f[10000],g[1600005],t[16],mi[16],a[16],d[16];
 10 struct edge { int to,from,v; }e[100];
 11 void insert(int x,int y,int z) { e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].v=z; head[x]=cnt; }
 12 void floyd()
 13 {
 14     for (int k=1;k<=n;k++)
 15         for (int i=1;i<=n;i++)
 16             for (int j=1;j<=n;j++)
 17                 dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
 18 }
 19 void zy_dp()
 20 {
 21     memset(f,inf,sizeof(f));
 22     f[0]=0;
 23     for (int i=0;i<mi[n];i++)
 24         for (int j=1;j<=n;j++)
 25             if (!(i&mi[j-1]))
 26                 for (int k=j+1;k<=n;k++)
 27                     if (!(i&mi[k-1]))
 28                         f[i^mi[j-1]^mi[k-1]]=min(f[i]+dis[j][k],f[i^mi[j-1]^mi[k-1]]);
 29 }
 30 void dp()
 31 {
 32     memset(g,inf,sizeof(g));
 33     g[2]=0; Q.push(2);
 34     while (!Q.empty())
 35     {
 36         int u=Q.front(),tot=0; Q.pop();
 37         for (int i=1;i<=n;i++) if (u/t[i-1]%3>0) a[++tot]=i;
 38         for (int i=1;i<=n;i++)
 39             if (u/t[i-1]%3==0)
 40             {
 41                 for (int j=head[i];j;j=e[j].from)
 42                     if (u/t[e[j].to-1]%3>0)
 43                     {
 44                         int k=u+t[i-1]*2;
 45                         if (g[u]>=g[k]) continue;
 46                         if (g[k]==inf) Q.push(k);
 47                         g[k]=g[u];
 48                     }
 49                 for (int j=1;j<=tot;j++)
 50                 {
 51                     int k=u+t[i-1];
 52                     k+=(u/t[a[j]-1]%3==1)?t[a[j]-1]:-t[a[j]-1];
 53                     if (g[u]+dis[i][a[j]]>=g[k]) continue;
 54                     if (g[k]==inf) Q.push(k);
 55                     g[k]=g[u]+dis[i][a[j]];
 56                 }
 57             }
 58     }
 59 }
 60 void calc()
 61 {
 62     int ans=inf;
 63     for (int i=0;i<t[n];i++)
 64     {
 65         int boo=0;
 66         for (int j=1;j<=n;j++) if (head[j]&&!((i/t[j-1])%3)) { boo=1; break; }
 67         if (boo) continue;
 68         int k=i;
 69         for (int j=1;j<=n;j++) if (d[j]%2==1) if (i/t[j-1]%3==1) k+=t[j-1]; else k-=t[j-1];
 70         int w=0;
 71         for (int j=1;j<=n;j++) if (k/t[j-1]%3==1) w^=mi[j-1];
 72         ans=min(ans,g[i]+f[w]);
 73     }
 74     for (int i=1;i<=cnt;i+=2) ans+=e[i].v;
 75     printf("%d",ans);
 76 }
 77 int main()
 78 {
 79     memset(dis,inf,sizeof(dis));
 80     scanf("%d%d",&n,&m);
 81     mi[0]=t[0]=1;
 82     for (int i=1;i<=n;i++) mi[i]=mi[i-1]*2,t[i]=t[i-1]*3;
 83     for (int i=1;i<=m;i++)
 84     {
 85         int x,y,z;
 86         scanf("%d%d%d",&x,&y,&z);
 87         dis[x][y]=dis[y][x]=min(dis[x][y],z);
 88         d[x]++; d[y]++;
 89         insert(x,y,z); insert(y,x,z);
 90     }
 91     scanf("%d",&m);
 92     for (int i=1;i<=m;i++)
 93     {
 94         int x,y,z;
 95         scanf("%d%d%d",&x,&y,&z);
 96         dis[x][y]=dis[y][x]=min(dis[x][y],z);
 97     }
 98     floyd(); zy_dp(); dp(); calc();
 99     return 0;
100 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/9291521.html