Hie with the Pie

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值