状压DP详解(3)状压DP的路径应用--旅行商问题(TSP)+简单例题POJ3311-Hie with the Pie

旅行商问题:假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

这种题貌似可以用dfs过。。。。也就是搜索所有的可能走法。。。想想总共有n!种方案数,只要n不大一般都ok,只不过需要加剪枝。。。。我的DFS比较菜。。。就只能老老实实写状压DP了。。。

这个状压DP的路径问题有别于之前在矩阵中的状压,之前我们是对于每一行的状态进行二进制枚举,然后直接暴力判断每行的可行状态并用dp存下答案。但这里并没有行。。。要怎么状态压缩就是个问题了。。。

一般的路径状压都是枚举已经走过的城市的状态,然后枚举目标城市进行DP。下面拿一道例题来讲解:题目来源:POJ3311

Hie with the Pie

Time Limit: 2000MS Memory Limit: 65536K

题目链接http://poj.org/problem?id=3311

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


题目大意:多组输入,遇到0退出,总共n+1行,每行n+1列,第i行的第j列代表i到j的距离。这些城市你可以重复走过,问你从0开始游遍整座城市最后回到0的最短时间。

虽然和标准的TSP有点差别,但问题是多变的。。。我们可以先用floyd求出每个城市到每个城市的最短路。这个比较简单就不多说了。

接下来对dp数组做一个规定,dp[i][j]代表状态i的目标城市为j的最短时间。也就是说它的最后一个经过城市为j。。。

然后开始枚举状态从1到11111:也就是只走过第一个城市到已经走完了所有的城市的状态,然后我们继续枚举目标城市,从1到n,对于只走过一个城市的状态我们就直接给出了:

dp[i][j]=dis[0][j];

这个不难理解,从0到j嘛。那么什么情况下只经过一个城市呢?很容易理解:

int tmp=1<<(j-1);
if (tmp==i) {
	dp[i][j]=dis[0][j];
	continue;
}

接下来就是枚举松弛点k了,看看能否由上一个状态通过松弛点来更新dp:

dp[i][j]=min(dp[i][j],dp[i^(1<<(j-1))][k]+dis[k][j]);

其中i^(1<<(j-1))就是将第j座城市丢掉的状态,相当于dp[i][j]的上一个状态。

以下是AC代码:

//#include <bits/stdc++.h>
#include <cstdio>
#include <algorithm>
using namespace std;
const int inf=9e8+10;
int dis[15][15];
int dp[1<<11][15];
int main()
{
    int n,m;
    while (scanf ("%d",&n)){
        if (!n) break;
        for (int i=0; i<=n; i++)
            for (int j=0; j<=n; j++)
                scanf ("%d",&dis[i][j]);
        for (int k=0; k<=n; k++)
            for (int i=0; i<=n; i++)
                for (int j=0; j<=n; j++)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        for (int i=1; i<(1<<(n+1)); i++){
            for (int j=1; j<=n; j++){
                int tmp=1<<(j-1);
                if (tmp==i){
                    dp[i][j]=dis[0][j];
                    continue;
                }
                if (!(tmp&i)) continue;//即tmp和i的01交错,i还未走过j城市
                dp[i][j]=inf;
                for (int k=1; k<=n; k++){
                    if (!(i&(1<<(k-1)))) continue;//i和k交错,i还未走过k城市
                    if (j==k) continue;
                    dp[i][j]=min(dp[i][j],dp[i^tmp][k]+dis[k][j]);
                }
            }
        }
        int x=(1<<n)-1,ans=inf;
        for (int i=1; i<=n; i++){//枚举已经走过的n个城市的最终点
            ans=min(ans,dp[x][i]+dis[i][0]);
        }
        printf ("%d\n",ans);
    }
    
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值