poj 3311 Hie with the Pie 【状态dp + 最短路floyd】

Hie with the Pie
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 5381 Accepted: 2880

Description

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

Input

Input will consist of multiple test cases. The first line will contain a single integer n indicating the number of orders to deliver, where 1 ≤ n ≤ 10. After this will be n + 1 lines each containing n + 1 integers indicating the times to travel between the pizzeria (numbered 0) and the n locations (numbers 1 to n). The jth value on the ith line indicates the time to go directly from location i to location j without visiting any other locations along the way. Note that there may be quicker ways to go from i to j via other locations, due to different speed limits, traffic lights, etc. Also, the time values may not be symmetric, i.e., the time to go directly from location i to j may not 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 indicating the 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

Sample Output

8


题意:有个人要给N个城市送N份披萨,出发点为0。给出一个N+1 * N+1 矩阵表示 0-N这 N+1个点之间的单向距离。问你从0依次送完N个披萨后返回到0的最短时间。


分析:把是否经过N个城市压缩成1<<N个状态,0表示没经过,1表示已经过。形象点的例子:若经过城市1、3、5、7、9但没有经过城市2、4、6、8、10表示为二进制0101010101(实际算的时候没有前导0)。

我们可以用dp[i][j]表示在j状态下到达城市i的最短时间。

则状态转移方程:dp[i][j] = min(dp[i][j], dp[k][Del(j, i)] + dist[k][i]).

Del(j, i)表示在状态j表示的二进制里删去i成立时对应二进制位置的1,实现为 j ^ (1<<(i-1))。也可以说是所有i不成立时的状态。这里我们又选取了一个城市k做中间点(和floyd求最短路思维一样)来更新最短时间。


实现过程:求Floyd处理最短路,然后枚举所有状态,对每种状态j更新到达所有城市的最短时间。

更新方案有二种处理

一:该状态只经过当前更新的城市i,那么有dp[i][j] = dist[0][i];

二:该状态还经过其他城市,选取中间城市进行更新。


最后处理:我们已经知道状态1<<(N-1)代表的含义---经过所有N个点

那么dp[i][1<<(N-1)]表示的含义是经过所有点后到达i最短时间,加上返回时间dist[i][0]就是该方案的最终结果。

因此我们只需用ans 存储第一个结果并做出如下更新就行了

int ans = dp[1][(1<<N)-1] + dist[1][0];

for(int i = 2; i <= N; i++)//遍历所有点

ans = min(ans, dp[i][(1<<N)-1] + dist[i][0]);


AC代码:


#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <algorithm>
#define INF 10000000
using namespace std;
int dist[12][12];//存储两点间最短路
int dp[12][1<<12];//dp[i][j]表示在j状态下到达第i城市的最短路
int N;//N份要送
int two(int x, int y)//判断两个状态是否有相邻的1
{
    if(x & y) return 1;//有的话返回一
    return 0;
}
int del(int x, int y)//在x状态中剔除y
{
	return x ^ (1<<(y));
}
void input()
{
    for(int i = 0; i <= N; i++)
    {
        for(int j = 0; j <= N; j++)
            scanf("%d", &dist[i][j]);
    }
}
void Floyd()
{
    for(int k = 0; k <= N; k++)
    {
        for(int i = 0; i <= N; i++)
        {
            for(int j = 0; j <= N; j++)
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
        }
    }
}
void solve()
{
    //memset(dp, 0, sizeof(dp));
    int total = 1 << N;//总状态数目
    for(int j = 0; j < total; j++)//枚举所有状态
    {
        for(int i = 1; i <= N; i++)//遍历所有城市
        {
        	if(two(j, 1<<(i-1)))//该状态经过城市i 
        	{
        		if(j == 1<<(i-1))//该状态只经过城市i
				dp[i][j] = dist[0][i];//最优解 为0到i的最短路 
				else//还经过其他城市
				{
					dp[i][j] = INF;
					for(int k = 1; k <= N; k++)//遍历
					{
						if(two(j, 1<<(k-1)) && k != i)//找到该状态中的不是城市i的其他城市 
						dp[i][j] = min(dp[i][j], dp[k][del(j, i-1)] + dist[k][i]);
						//在没经过i的状态中 寻找中间点k 更新最短路 
					} 
				} 
        	}
        }
    }
    int ans = dp[1][(1<<N)-1] + dist[1][0];
    for(int i = 2; i <= N; i++)//遍历所有城市
	ans = min(ans, dp[i][(1<<N)-1] + dist[i][0]);
	printf("%d\n", ans);
}
int main()
{
    while(scanf("%d", &N), N)
    {
        input();
        Floyd();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值