zoukankan      html  css  js  c++  java
  • [欧拉回路][状压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与一条必须走的路径与连通图相连
    • 因为这条边必须被经过一次,那么这条边不产生额外代价
    • ②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 }
  • 相关阅读:
    数组地址,数组首地址与数组首元素地址的区别
    memset,memcpy与strcpy
    OJ之大数与高精度题必备知识
    OJ之星期几算法(泽勒一致性)
    二分查找及其优化
    爱上vim之快捷键使用技巧与个性化配置
    shell之终极shell——zsh
    memset的一些坑
    OO终章
    hOmewOrk 第三单元 总结
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9291521.html
Copyright © 2011-2022 走看看