题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4479

Description

【故事背景】
作为JSOI的著名吃货,JYY的理想之一就是吃遍全世界的美食。要走遍全世界当然需要不断的坐飞机了。而不同的航班上所提供的餐食是很不一样的:比如中国的航班会提供中餐,英国的航班有奶茶和蛋糕,澳大利亚的航班有海鲜,新加坡的航班会有冰激凌……JYY选出了一些他特别希望品尝餐食的航班,希望制定一个花费最少的旅游计划,能够从南京出发,乘坐所有这些航班并最后回到南京。

【问题描述】

世界上一共有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描述可以乘坐也可以不乘坐的航班信息。
2<=N<=13,0<=K<=78,2<=M<=200,1<=x,y<=N,1<=z<=10^4

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
一个可行的最佳方案为1->2->3->5->4->1。
机票所需的费用为1000+1000+300+500+300=3100
 

题目大意,给一张有权无向图,标记其中的某些边。从点1出发要求我们找一条回路,且这条回路经过所有的标记边,最小化消耗的费用

考虑回路的性质,因为是在无向图中,只要我们的边集里的每个点度数都是偶数,那么就一定有一种方法形成回路

于是我们就枚举一个联通子图,注意到n的值很小,313≈1600000。咳咳,这是完全没有问题的。然后我们考虑从结点1开始向点集里面加点。状态压缩的时候,我们定义0表示当前点不在点集里,1表示在点集里且度数为奇数,2表示在点集里且度数是偶数。加点的时候我们有两种情况:

1.这个点是一条必须边的端点,且另一个端点在点集里,那么这条边我们一定要加进去。但注意我们此时并不用添加代价,所有必须边的代价放到最后加入(代价同时也包括点的度数)

2.枚举点集中的点,这个点与枚举的点存在一条路径,我们取最短路添加进去就好(最短路在之前用floyd先预处理好),同时我们要算上最短路的代价,改变点度数的奇偶性。值得注意的是,读者可能会有以下两个疑惑:

  ①要是最短路路径上的点不在我们枚举的点集里面怎么办?首先我们要明确,在连上最短路的时候,路径上的点奇偶性是不会改变的。其次,就算有些点不在点集里,我们要的只是当前这个状态,换言之就是这个结        果,不需要考虑中间的过程。而在之后的操作里我们也发现还需要的代价值取决于度数为奇数的点,路径上的点不会有影响。

  ②要是最短路经过了点集里的必须边呢?我们是不是多算了代价?这些边的端点的奇偶性是不是也变了?其实都没有,其实这道题说是欧拉回路的话我们的定义还是存在偏差,可以理解为我们重复经过了这个点,或者说多连了两条边在上面,奇偶性没有影响。再者航班是来回几次就要付出多少倍的钱的。读者模拟几张图发现出现上述情况的话我们的回路都必须绕回这个点,因此最短路付出的代价是必要的。

接下来我们其实就得到每一个状态形成的最小代价,但是这样还不行,我们还要让每一个点的度数都是偶数。于是我们必须把奇数点两两匹配,这个的代价我们也可以用g数组预处理好。(若是出现疑问可以参考上面的①②点),当然别忘了我们还有必须边的代价没有计算,此时一起算上去,ans取min值就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std; const int maxn=;
const int mxn=+;
const int inf=0x3f3f3f3f;
int n,m,tot,ans;
int head[maxn],bin[maxn],pin[maxn],g[<<maxn],dist[maxn][maxn],f[],deg[maxn],a[maxn];
struct EDGE
{
int to;int next;int l;
}edge[mxn<<];
inline int read()
{
char ch=getchar();
int s=,f=;
while (!(ch>=''&&ch<='')) {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
return s*f;
}
void init()
{
memset(g,inf,sizeof(g));
memset(dist,inf,sizeof(dist));
memset(f,inf,sizeof(f));
for (int i=;i<=n;i++) dist[i][i]=;
bin[]=pin[]=;
for (int i=;i<=n;i++) bin[i]=bin[i-]*,pin[i]=pin[i-]*;
}
void add(int x,int y,int l)
{
edge[++tot]=(EDGE){y,head[x],l};
head[x]=tot;
edge[++tot]=(EDGE){x,head[y],l};
head[y]=tot;
}
void floyd()//最短路
{
for (int k=;k<=n;k++)
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
void pre_dp()//需处理奇数点匹配的代价
{
g[]=;
for (int i=;i<bin[n];i++)
for (int j=;j<=n;j++)
if (!(i&bin[j-]))
for (int k=j+;k<=n;k++)
if (!(i&bin[k-]))
g[i^bin[j-]^bin[k-]]=min(g[i^bin[j-]^bin[k-]],g[i]+dist[j][k]);
}
void dp()//枚举状态,并转移
{
queue <int> q;
f[]=;q.push();
while (!q.empty())
{
int s=q.front();q.pop();
int cnt=;
for (int i=;i<=n;i++) if (s/pin[i-]%>) a[++cnt]=i;
for (int i=;i<=n;i++)
if (s/pin[i-]%==)//注意三进制状态压缩的方法
{
for (int j=head[i];j;j=edge[j].next)
if (s/pin[edge[j].to-]%>)
{
int s1=s+pin[i-]*;//必须点的度数一开始不算,视为0
if (f[s]>=f[s1]) continue;
if (f[s1]==inf) q.push(s1);
f[s1]=f[s];//更新
}
for (int j=;j<=cnt;j++)
{
int s1=s+pin[i-];
s1+=(s/pin[a[j]-]%==)?pin[a[j]-]:-pin[a[j]-];//改变度数
if (f[s]+dist[i][a[j]]>=f[s1]) continue;
if (f[s1]==inf) q.push(s1);
f[s1]=f[s]+dist[i][a[j]];
}
}
}
}
void calc()//统计答案
{
ans=inf;
for (int s=;s<=pin[n]-;s++)
{
int flag=;
for (int i=;i<=n;i++) if (deg[i]&&s/pin[i-]%==) {flag=;break;}//如果就必须点没加就直接跳过这个状态
if (flag) continue;
int now=s;
for (int i=;i<=n;i++) if (deg[i]&) now+=(s/pin[i-]%==)?pin[i-]:-pin[i-];//考虑必须边对必须点的度数影响
int s1=;
for (int i=;i<=n;i++) if (now/pin[i-]%==) s1^=bin[i-];
ans=min(ans,f[s]+g[s1]);
}
for (int i=;i<=tot;i+=) ans+=edge[i].l;
}
int main()
{
n=read();m=read();
init();
while (m--)
{
int x=read(),y=read(),l=read();
dist[x][y]=dist[y][x]=min(dist[x][y],l);
deg[x]++;deg[y]++;
add(x,y,l);
}
m=read();
while (m--)
{
int x=read(),y=read(),l=read();
dist[x][y]=dist[y][x]=min(dist[x][y],l);
}
floyd();
pre_dp();
dp();
calc();
printf("%d\n",ans);
return ;
}

BZOJ4479 [JSOI2013] 吃货jyy 解题报告(三进制状态压缩+欧拉回路)的更多相关文章

  1. BZOJ4479 : [Jsoi2013]吃货jyy

    若$k\leq 15$,那么可以设$d[i][S]$表示经过了$S$集合的边,现在位于$i$点的最短路. 可以用Dijkstra算法在$O(n^22^k)$时间内求出. 否则若$k>15$,那么 ...

  2. hdu4064 三进制状态压缩 好题!

    还不太会做这类题,总之感觉有点难啊. 用深搜代替打表求出一行所有的可行状态,注意要进行剪枝 这是自己理解的代码,但是tle了 #include<bits/stdc++.h> using n ...

  3. hdu-3001 三进制状态压缩+dp

    用dp来求最短路,虽然效率低,但是状态的概念方便解决最短路问题中的很多限制,也便于压缩以保存更多信息. 本题要求访问全图,且每个节点不能访问两次以上.所以用一个三进制数保存全图的访问状态(3^10,空 ...

  4. HDU 3001 Travelling (三进制状态压缩 DP)

    题意:有 n 个city,能够选择任一城市作为起点,每一个城市不能訪问超过2次, 城市之间有权值,问訪问所有n个城市须要的最小权值. 思路:由于每一个城市能够訪问最多两次,所以用三进制表示訪问的状态. ...

  5. 三进制状态压缩DP(旅行商问题TSP)HDU3001

    http://acm.hdu.edu.cn/showproblem.php?pid=3001 Travelling Time Limit: 6000/3000 MS (Java/Others)     ...

  6. BZOJ 4479: [Jsoi2013]吃货jyy

    一句话题意:求必须包含某K条边的回路(回到1),使得总权值最小 转化为权值最小的联通的偶点 令F[i]表示联通状态为i的最小权值,(3^n状压)表示不在联通块内/奇点/偶点,连边时先不考虑必选的边的度 ...

  7. HDOJ-3001(TSP+三进制状态压缩)

    Traving HDOJ-3001 这题考察的是状态压缩dp和tsp问题的改编 需要和传统tsp问题区分的事,这题每个点最多可以经过两次故状态有3种:0,1,2 这里可以模仿tsp问题的二进制压缩方法 ...

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

    正题 题目链接:https://www.luogu.com.cn/problem/P6085 题目大意 \(n\)个点的一张无向图,有\(k\)条必走边,\(m\)条其他边,求从\(1\)出发经过必走 ...

  9. hdu 3001 Travelling (三进制)【状压dp】

    <题目链接> 题目大意: 给出n个点和m条边,求经过所有点所需的最小花费,每个点最多经过两次. 解题分析: TSP问题类型,由于此题每个点有三种状态,所以采用三进制状态压缩,0.1.2 分 ...

随机推荐

  1. Java的接口总结

    Java最主要的封装是class.除此之外还有接口interface. 这段时间一直在想接口有什么作用呢.有了接口有哪些优点呢.结合网络上各位大神的文章,接口的作用大概体如今下面几个方面. 1.回调 ...

  2. 对Shell几个冷知识的总结(IFS,数组,替换,分割,查找)

    IFS: 对IFS的用处直接进行说明,详细IFS是干什么的...自行谷歌 首先创建一个 "a a",和"a"的文件: 然后我们 ls查看一下: --> l ...

  3. Linux下DNS服务器搭建详解

    Linux下DNS服务器搭建详解 DNS  即Domain Name System(域名系统)的缩写,它是一种将ip地址转换成对应的主机名或将主机名转换成与之相对应ip地址的一种机制.其中通过域名解析 ...

  4. [codeforces 852 D] Exploration Plan 解题报告 (二分+最大匹配)

    题目链接:http://codeforces.com/problemset/problem/852/D 题目大意: 有V个点,N个队伍,E条边,经过每条边有个时间,告诉你初始N个队伍的位置,求至少有K ...

  5. rsync来传输文件(可断点续传)

    scp传文件的话如果出错就得重新来过, 用rsync可以实现断点上传的功能   大概就是这样用:  rsync -P --rsh=ssh home.tar 192.168.205.34:/home/h ...

  6. 蛋白质GO信息的一些数据库

    最近用到蛋白质序列数据,但是才发现蛋白质的编号主要分为两种:一种是ENSP开头,主要是在ensembl数据库查询asia.ensembl.org/Human/Search:而另一种是uniprot.w ...

  7. 【原创】MemCached中的参数解释

    优化MemCached的主要目的为增加命中率和提高内存使用率,在优化的时候可以根据以下参数综合考虑分析: 首先是进程项: pid Memcached进程ID uptime Memcached运行时间, ...

  8. 《剑指offer》数组中出现次数超过一半的数字

    一.题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出 ...

  9. STM8S103内存详析

    STM8S103的RAM有1k,0x00-0x3FF(RAM和ROM统一编址),其中0x200-0x3ff共512个字节默认为堆栈,剩余的低端512个字节又分为了Zero Page和剩余的RAM(简称 ...

  10. Codeforces Round #289 Div 2

    A. Maximum in Table 题意:给定一个表格,它的第一行全为1,第一列全为1,另外的数满足a[i][j]=a[i-1][j]+a[i][j-1],求这个表格中的最大的数 a[n][n]即 ...