LightOJ1086 Jogging Trails(欧拉回路+中国邮递员问题+SPFA)
题目求从某点出发回到该点经过所有边至少一次的最短行程。
- 这个问题我在《图论算法理论、实现及应用》中看过,是一个经典的问题——中国邮递员问题(CPP, chinese postman problem)也称为中国邮路问题,是我国数学家管梅谷教授于1962年首次提出的,引起了世界不少数学家的关注。例如1973年匈牙利数学家Edmonds和Johnsom对中国邮路问题提出了一种有效算法。
- 解决的方法就是通过添加边,使其成为欧拉回路。
而这一题的问题就可以转化给这个无向图加最短的边使其所有点的度位偶数(为什么不说欧拉回路,因为题目没说图一定连通。。
这道题在LightOJ分类是DP。。我想大概就是15个点二进制压缩的状压DP。也就是把各点度为偶数奇数分别看作二进制各位0和1,然后每次可以通过在任意两个奇度或偶度点加一条边来转移。。
不过显然这是不行的,这样的状态不满足DP的无后效性,状态是点转移是边那就可以是个有向有环图——有向有环图——于是我就想到最短路,显然可以用最短路做,不过时间复杂度不好说。。具体来说:
- 偶度点记0奇度记1压缩成的二进制就是要求最短路的图中的顶点,这样顶点最多215个;
- 而如果两个顶点有且仅有i和j这两位的二进制不同且原图中i到j有路径,那么这两个顶点间有一条边且权为原图中i到j中最短的那条路径;
- 最短路的起点就是原图度序列压缩表示的顶点,终点就是0。
如此构图,跑个SPFA,然后就AC了。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 using namespace std; 5 #define INF (1<<30) 6 int n,map[15][15]; 7 int d[1<<15]; 8 bool vis[1<<15]; 9 int SPFA(int vs){ 10 for(int i=0; i<(1<<15); ++i){ 11 d[i]=INF; vis[i]=0; 12 } 13 d[vs]=0; vis[vs]=1; 14 queue<int> que; 15 que.push(vs); 16 while(!que.empty()){ 17 int u=que.front(); que.pop(); 18 for(int i=0; i<n; ++i){ 19 for(int j=i+1; j<n; ++j){ 20 if(map[i][j]==-1) continue; 21 int v=u; v^=(1<<i); v^=(1<<j); 22 if(d[v]>d[u]+map[i][j]){ 23 d[v]=d[u]+map[i][j]; 24 if(!vis[v]){ 25 vis[v]=1; 26 que.push(v); 27 } 28 } 29 } 30 } 31 vis[u]=0; 32 } 33 return d[0]; 34 } 35 int main(){ 36 int t,m,a,b,c; 37 scanf("%d",&t); 38 for(int cse=1; cse<=t; ++cse){ 39 scanf("%d%d",&n,&m); 40 memset(map,-1,sizeof(map)); 41 int deg[15]={0},res=0; 42 while(m--){ 43 scanf("%d%d%d",&a,&b,&c); 44 --a; --b; 45 ++deg[a]; ++deg[b]; res+=c; 46 if(map[a][b]==-1 || map[a][b]>c) map[a][b]=map[b][a]=c; 47 } 48 int vs=0; 49 for(int i=0; i<n; ++i){ 50 if(deg[i]&1) vs|=(1<<i); 51 } 52 res+=SPFA(vs); 53 printf("Case %d: %d\n",cse,res); 54 } 55 return 0; 56 }