POJ 2404 Jogging Trails 欧拉回路+状压DP

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89520951

title

POJ 2404
Description

Gord is training for a marathon. Behind his house is a park with a large network of jogging trails connecting water stations. Gord wants to find the shortest jogging route that travels along every trail at least once.

Input

Input consists of several test cases. The first line of input for each case contains two positive integers: n <= 15, the number of water stations, and m < 1000, the number of trails. For each trail, there is one subsequent line of input containing three positive integers: the first two, between 1 and n, indicating the water stations at the end points of the trail; the third indicates the length of the trail, in cubits. There may be more than one trail between any two stations; each different trail is given only once in the input; each trail can be travelled in either direction. It is possible to reach any trail from any other trail by visiting a sequence of water stations connected by trails. Gord’s route may start at any water station, and must end at the same station. A single line containing 0 follows the last test case.

Output

For each case, there should be one line of output giving the length of Gord’s jogging route.

Sample Input

4 5
1 2 3
2 3 4
3 4 5
1 4 10
1 3 12
0

Sample Output

41

Source

Waterloo local 2002.07.01

analysis

先给一个题目大意:

n n 个点, m m 条无向边,每条边有一定的距离数值,构造成一个连通图。问从任意一点出发,遍历所有的边,每条边至少访问一次,再回到起点,求满足要求的方案中走过的距离之和的最小短值。

通过这个简单的题目大意,我们就想到了欧拉回路。欧拉回路的充要条件:所有的点都必须为偶数度。
那么情况分为两类:

  • 如果这个图本身就是欧拉回路,那么答案显然就是所有的边权和。
  • 如果这个图不是欧拉回路,
    • 由于是连通图,根据握手定理的推论则必有偶数个点的度为奇数。要从一点出发每边至少走一次,则必须要构成一个欧拉回路,所以有些边必须要走多次,
    • 每多走一次等价多连接了一条边,这样构成欧拉图,原先的边和新加的虚拟边在欧拉图中有且仅经过一次
    • 现在还要使距离之和最短。原先的边的距离之和是固定的了,要使结果最小,只能使新加的虚拟边之和最小。
    • 通过分析可以发现要构成欧拉图,添加的虚拟边的两个端点原先的度数一定是奇数,如果其中有偶数度的点,添加一边后度数就会变奇数,不可能成为欧拉图或者多此一举。
    • 于是现在的问题是如何在偶数个奇度顶点中两两连线,使得这些连线的距离之和最小,易想到两个顶点的连线长度应该是这两点间的最短距离(贪心)。
    • 想要解决这个问题,可以使用最优匹配算法,也可以使用动态规划我用DP啦,qwq
      • 用一个二进制 01 01 表示点度情况, 0 0 表示偶数度, 1 1 表示奇数度。
      • f [ i ] f[i] 表示状态为 i i 时的最小距离。
      • 状态转移方程: f [ i ] = m a x { f [ i ] + d i s t [ i ] [ i ] } f[i&#x27;]=max\left\{f[i]+dist[i][i&#x27;]\right\} (条件: i i 状态可以转化到 i i&#x27; 状态, d i s t [ i ] [ i ] dist[i][i&#x27;] 表示 添加的虚拟边的最小距离 )。
      • 答案:所有的边权和 + f [ 2 t o p 1 ] ( t o p ) +f[2^{top}-1](top为奇数度点的数量)

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=16;

template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

int n,m,dist[maxn][maxn];
inline void folyed()
{
	for (int k=1; k<=n; ++k)
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=n; ++j)
				dist[i][j]=min(dist[i][k]+dist[k][j],dist[i][j]);
}

int f[1<<maxn];
int q[maxn],top,tot;
inline void Dp()
{
	memset(f,0x3f,sizeof(f));
	f[0]=0;
	for (int i=0; i<=(1<<top)-1; ++i)
	{
		int x=1;
		while ((1<<x-1)&i) ++x;
		for (int y=x+1; y<=top; ++y) if ( !(i & (1<<y-1) ) )
			f[i | (1<<y-1) | (1<<x-1)]=min(f[i | (1<<y-1) | (1<<x-1)],f[i]+dist[q[x]][q[y]]);
	}
}

int deg[maxn];
int main()
{
	while (1)
	{
		read(n);
		if (!n) break;
		read(m);
		memset(dist,0x3f,sizeof(dist));
		memset(deg,0,sizeof(deg));
		tot=top=0;
		for (int i=1; i<=m; ++i)
		{
			int x,y,z;
			read(x);read(y);read(z);
			dist[y][x]=dist[x][y]=min(dist[x][y],z);
			++deg[x],++deg[y],tot+=z;
		}
		folyed();
		for (int i=1; i<=n; ++i)
			if (deg[i]&1) q[++top]=i;
		Dp();
		printf("%d\n",f[(1<<top)-1]+tot);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/89520951