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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章