Hie with the Pie
Time Limit: 2000MS | | Memory Limit: 65536K | Total Submissions: 3197 | | Accepted: 1636 |
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 then 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 Source
/**
题目大意:
给定要送匹萨的N个点和匹萨店0点的距离关系,问在从匹萨店出发,送完N个地方后,又回到0点所需的最小路程,
某条路可以重复走~~~
状态压缩DP:
1,先求最短路,再进行dp
2,从初状态开始入队,每次枚举N+1个方向,dp,得到的新状态再次入队
**/
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <vector>
#include <bitset>
#include <cstdio>
#include <string>
#include <numeric>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int dx[4]= {-1,1,0,0};
int dy[4]= {0,0,-1,1}; //up down left right
bool inmap(int x,int y,int n,int m)
{
if(x<1||x>n||y<1||y>m)return false;
return true;
}
int hashmap(int x,int y,int m)
{return (x-1)*m+y;}
#define eps 1e-8
#define inf 0x7fffffff
#define debug puts("BUG")
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define read freopen("in.txt","r",stdin)
#define write freopen("out.txt","w",stdout)
#define maxn 5000
int dis[22][22];
int N;
int dp[22][maxn];
int via[22][maxn];
int ans;
class Node
{
public:
int s;
int d;
Node(int d1,int s1){d=d1;s=s1;}
Node(){}
};
void init()
{
memset(dis,0,sizeof(dis));
memset(dp,0,sizeof(dp));
ans=0;
}
void floyd()//方案1 求最短路
{
for(int i=0; i<=N; i++)
for(int j=0; j<=N; j++)
for(int k=0; k<=N; k++)
if(dis[j][k]>dis[j][i]+dis[i][k])
dis[j][k]=dis[j][i]+dis[i][k];
}
void Queue()//方案2 队列
{
memset(dp,-1,sizeof(dp));//便于判断某一个dp是否更新
dp[0][0]=0; //初状态
queue<Node>q;
q.push(Node(0,0)); //初状态入队
via[0][0]=1; //表示初状态入队
while(!q.empty())
{
Node now=q.front();
q.pop();
via[now.d][now.s]=0;//表示now状态已经出队
for(int i=0;i<=N;i++)//向N+1个方向移动(扩展)
{
if(now.d==i) //相当于原地不动
continue;
if(dp[i][now.s|(1<<i)]==-1||dp[i][now.s|(1<<i)]>dp[now.d][now.s]+dis[now.d][i])
{//下一个状态,即到达i点后的状态,变为now.s|(1<<i),后的dp进行更新
dp[i][now.s|(1<<i)]=dp[now.d][now.s]+dis[now.d][i];
if(!via[i][now.s|(1<<i)])//该点已更新,根据该点可以更新由它转移到的其他状态,如果它未在队列,需要入队
{
via[i][now.s|(1<<i)]=1;//标记入队
q.push(Node(i,now.s|(1<<i)));//入队
}
}
}
}
ans=dp[0][(1<<(N+1))-1];//1<<(N+1))-1=1111...1(2) 表示从0点到N点都已经走过了,dp[0][(1<<(N+1))-1]表示最后的状态又要回到0点
}
void DP()//方案2 枚举所有状态
{
int End=(1<<N)-1;//末状态
for(int s=0; s<=End; s++)//枚举所有状态(注意,这些状态中不包括0点,默认从零点出发)
for(int i=1; i<=N; i++)//N个方向
if(s&(1<<(i-1)))//该状态下,已经走过i位置(因为状态的第0位存的是第1个地方的状态)
{
if(s==(1<<(i-1)))//初始条件,表示只走过这个点
dp[i][s]=dis[0][i];//默认是从0点出发,因此,0点已经走过,不需要在状态中表示0点是否走过,
else//如果不是初始条件,则为其他条件转化过来的
{
dp[i][s]=inf;
for(int j=1; j<=N; j++)//从其他位置找一个作为中转站,更新dp
{
int last=s^(1<<(i-1));
if(s&(1<<(j-1))&&j!=i)//表示上一个状态走过j点,并且i点不为j点
dp[i][s]=min(dp[i][s],dp[j][last]+dis[j][i]);
}
}
}
ans=inf;
for(int i=1;i<=N;i++)//从末状态End在回到0点,末状态时送披萨的人可能在其他的任何一个点上,取这些点中的最小值
ans=min(dp[i][End]+dis[i][0],ans);
}
void print()
{printf("%d\n",ans);}
int main()
{
while(~scanf("%d",&N)&&N)
{
init();
for(int i=0; i<=N; i++)
for(int j=0; j<=N; j++)
scanf("%d",&dis[i][j]);
{floyd();DP();}//方案1
{Queue();}//方案2
print();
}
return 0;
}
|