【问题描述】
披萨店为其能尽可能快地将披萨送到顾客手中感到骄傲。不幸的是,由于削减开支,现在他
们只能雇佣一个司机来送披萨。这个司机在送披萨之前,要等 1 个或多个(最多 10 个)要
处理的订单。司机希望送货和返回披萨店能走最短的路线,即使路上会走过同一地点或经过
披萨店多次。现在请你编写一个程序来帮助这个司机。
【输入格式】
输入由多个测试用例组成。第一行给出一个整数 n,表示要送货的订单数,1<=n<=10。然后
有 n+1 行,每行给出 n+1 个整数,表示披萨店(编号为 0)和 n 个地点(编号从 1 到 n)之
间到达所用的时间。在第 i 行的第 j 个值表示从地点 i 直接到地点 j,在路上不去其他地点的
时间。注意,由于不同的速度限制和红绿灯,从 i 到 j 通过其他地点可能会比直接走更快;
而且,时间值可能不对称,也就是说,直接从地点 i 到 j 所用的时间可能和从地点 j 到地点 i
所用的时间不一样。输入以 n=0 终止。
【输出格式】
对每个测试用例,输出一个数,表示送完所有的披萨并返回披萨店所用的最少时间。
【输入样例】
3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0
【输出样例】
8
分析:
可以用全排列做,求出一个最短的距离即可。或者用状态压缩DP.用一个二进制数表示城市是否走过
【状态表示】dp[state][i]表示到达i点状态为state的最短距离
【状态转移方程】dp[state][i] =min{dp[state][i],dp[state'][j]+dis[j][i]} dis[j][i]为j到i的最短距离
【DP边界条件】dp[state][i] =dis[0][i] state是只经过i的状态
代码:
#include<iostream>
#define INF 100000000
using namespace std;
int dis[12][12];
int dp[1<<11][12];
int n,ans,_min;
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n) && n)
{
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)
if(dis[i][k] + dis[k][j]< dis[i][j])
dis[i][j] = dis[i][k] +dis[k][j];
for(int S = 0;S <= (1<<n)-1;++S)//枚举所有状态,用位运算表示
for(int i = 1;i <= n;++i)
{
if(S & (1<<(i-1)))//状态S中已经过城市i
{
if(S ==(1<<(i-1))) dp[S][i] =dis[0][i];//状态S只经过城市I,最优解自然是从0出发到i的dis,这也是DP的边界
else//如果S有经过多个城市
{
dp[S][i] = INF;
for(int j = 1;j <=n;++j)
{
if(S &(1<<(j-1)) && j != i)//枚举不是城市I的其他城市
dp[S][i] =min(dp[S^(1<<(i-1))][j] + dis[j][i],dp[S][i]);
//在没经过城市I的状态中,寻找合适的中间点J使得距离更短
}
}
}
}
ans = dp[(1<<n)-1][1] + dis[1][0];
for(int i = 2;i <= n;++i)
if(dp[(1<<n)-1][i] + dis[i][0] < ans)
ans = dp[(1<<n)-1][i] +dis[i][0];
printf("%d\n",ans);
}
return 0;
}