POJ 3311 Hie with the Pie(状压DP)

题目来源:http://poj.org/problem?id=3311

Hie with the Pie

Time Limit: 2000MS

Memory Limit: 65536K

Total Submissions: 9207

Accepted: 5007

Description

The Pizazz Pizzeria prides itself in delivering pizzas to itscustomers as fast as possible. Unfortunately, due to cutbacks, they can affordto hire only one driver to do the deliveries. He will wait for 1 or more (up to10) orders to be processed before he starts any deliveries. Needless to say, hewould like to take the shortest route in delivering these goodies and returningto the pizzeria, even if it means passing the same location(s) or the pizzeriamore than once on the way. He has commissioned you to write a program to helphim.

Input

Input will consist of multiple test cases. The first line willcontain a single integer n indicating the number of orders todeliver, where 1 ≤ n ≤ 10. After this will be n +1 lines each containing n + 1 integers indicating the times totravel between the pizzeria (numbered 0) and the n locations(numbers 1 to n). The jth value on the ithline indicates the time to go directly from location i to location j withoutvisiting any other locations along the way. Note that there may be quicker waysto go from ito j via other locations, due todifferent speed limits, traffic lights, etc. Also, the time values may not besymmetric, i.e., the time to go directly from location i to jmaynot be the same as the time to go directly from location j to i.An input value of n = 0 will terminate input.

Output

For each test case, you should output a single number indicatingthe minimum time to deliver all of the pizzas and return to the pizzeria.

Sample Input

3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0

SampleOutput

8

Source

East Central North America2006

 -----------------------------------------------------

思路

状态压缩DP

用二进制数s表示状态,s的第i个二进制位1/0表示是/否经过第i个节点。由于存在起始节点的关系,在本题中s的第(i-1)位与第i个节点相关联,以下按本题的关系进行说明。

s & (1<<(i-1)) == 0 ?: /是经过第i个节点

s ^ (1<<(i-1)) == 0?: 除第i个节点外否/是还经过其他节点

动归数组:dp[s][i],  s: 二进制状态,i: 当前到达的城市

状态转移方程:

dp[s][i] = min(dp[s-(1<<(i-1))][j] +mat[j][i]), j是所有除i以外s中还经过的城市

-----------------------------------------------------

代码 

// 可以重复经过同一点的旅行商问题

#include<iostream>
#include<fstream>
#include<climits>
#include<cstring>
using namespace std;

int mat[12][12] = {};
int dp[(1<<11)][12] = {};					// s: 二进制状态, i: 当前到达的城市, s&(1<<(i-1))表示已经经过城市i

int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin ("3311.txt");
	int n,i,j,k,ans,s;
	while (fin >> n)
	{
		if (n==0)
		{
			break;
		}
		memset(dp, 0, sizeof(dp));
		for (i=0; i<=n; i++)
		{
			for (j=0; j<=n; j++)
			{
				fin >> mat[i][j];
			}
		}
		// Floyd算法求点对点最短路
		for (i=0; i<=n; i++)
		{
			for (j=0; j<=n; j++)
			{
				for (k=0; k<=n; k++)
				{
					if (k!=i && k!=j && mat[i][k]+mat[k][j]<mat[i][j])
					{
						mat[i][j] = mat[i][k]+mat[k][j];
					}
				}
			}
		}
		// 状压DP
		for (s=1; s<(1<<n); s++)
		{
			for (i=1; i<=n; i++)
			{
				if (s & (1<<(i-1)))				// 该状态已经经过城市i
				{
					if (s == (1<<(i-1)))		// 如果直接从比萨店到了城市i
					{
						dp[s][i] = mat[0][i];
					}
					else						// 如果从比萨店到城市i中间经过了其他城市
					{
						dp[s][i] = INT_MAX/2;
						for (j=1; j<=n; j++)
						{
							if (s & (1<<(j-1)) && j!=i)			// 遍历经过的城市j
							{
								dp[s][i] = min(dp[s][i], dp[s-(1<<(i-1))][j] + mat[j][i]);	// 取各种路线中最近的一条
							}
						}
					}
				}
			}
		}
		// dp[(1<<n)-1][i]只是表示经过了所有点, 最后还需要返回比萨店
		ans = INT_MAX/2;
		for (i=1; i<=n; i++)
		{
			ans = min(ans, dp[(1<<n)-1][i] + mat[i][0]);
		}
		cout << ans << endl;
	}
	fin.close();
#endif
#ifdef ONLINE_JUDGE
	int n,i,j,k,ans,s;
	while (cin >> n)
	{
		if (n==0)
		{
			break;
		}
		memset(dp, 0, sizeof(dp));
		for (i=0; i<=n; i++)
		{
			for (j=0; j<=n; j++)
			{
				cin >> mat[i][j];
			}
		}
		// Floyd算法求点对点最短路
		for (i=0; i<=n; i++)
		{
			for (j=0; j<=n; j++)
			{
				for (k=0; k<=n; k++)
				{
					if (k!=i && k!=j && mat[i][k]+mat[k][j]<mat[i][j])
					{
						mat[i][j] = mat[i][k]+mat[k][j];
					}
				}
			}
		}
		// 状压DP
		for (s=1; s<(1<<n); s++)
		{
			for (i=1; i<=n; i++)
			{
				if (s & (1<<(i-1)))				// 该状态已经经过城市i
				{
					if (s == (1<<(i-1)))		// 如果直接从比萨店到了城市i
					{
						dp[s][i] = mat[0][i];
					}
					else						// 如果从比萨店到城市i中间经过了其他城市
					{
						dp[s][i] = INT_MAX/2;
						for (j=1; j<=n; j++)
						{
							if (s & (1<<(j-1)) && j!=i)			// 遍历经过的城市j
							{
								dp[s][i] = min(dp[s][i], dp[s-(1<<(i-1))][j] + mat[j][i]);	// 取各种路线中最近的一条
							}
						}
					}
				}
			}
		}
		// dp[(1<<n)-1][i]只是表示经过了所有点, 最后还需要返回比萨店
		ans = INT_MAX/2;
		for (i=1; i<=n; i++)
		{
			ans = min(ans, dp[(1<<n)-1][i] + mat[i][0]);
		}
		cout << ans << endl;
	}
#endif
}


猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/80962184