HDU - 4281 Judges' response (mtsp,tsp+01揹包)

題意

有n個地點,每個旅行商在地點上花費最多m,每個地點座標(xi,yi),花費Ci

1.最少幾個旅行商可以走過所有點;
2.所有旅行商走完回到起點的路程長度的總和最小是多少;

思路

第一問

轉移方程:dp[i]=min(dp[i],dp[i^state]+1);

第二問

轉移方程:dp[i]=min(dp[i],dp[i^state]+cost[state]);
用解tsp的方法預處理出各種狀態路程長度的花費,存在cost裏;
對於每種狀態求出是否滿足解題時間不大於m存在state裏;
最後把每種可行的state當作物品,全都取當作總容量,用01揹包解法解出兩個結果;

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=17,M=(1<<16)+1;
const int INF=0x3f3f3f3f;

struct Point
{
    int x,y,cost;
} point[20];
int G[N][N];//存鄰接矩陣
int state[M];//存單個旅行商狀態
int cost[M];//存該狀態的花費
int dp[M][N];
int n,m,cnt;

int lengh(int a,int b)
{
    double sum=(double)(point[a].x-point[b].x)*(point[a].x-point[b].x)+(point[a].y-point[b].y)*(point[a].y-point[b].y);
    return ceil(sqrt(sum));
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; i++)
            scanf("%d %d",&point[i].x,&point[i].y);
        for(int i=0; i<n; i++)
            scanf("%d",&point[i].cost);

        //預處理出鄰接矩陣
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                G[i][j]=lengh(i,j);

        //求i狀態以j爲終點旅行商最少花費(路長花費);
        memset(dp,0x3f,sizeof dp);
        dp[1][0]=0;
        for(int i=0; i<(1<<n); i++)
            for(int j=0; j<n; j++)
                if(i&(1<<j))
                    for(int k=0; k<n; k++)
                        if((i^(1<<j))&(1<<k))
                            dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+G[k][j]);

        //求狀態i旅行商最少花費(路長花費)(合併時我們不關心單個旅行商的終點在哪)
        memset(cost, 0x3f,sizeof cost);
        for(int i=0; i<(1<<n); i++)
            for(int j=0; j<n; j++)
                if((i|1)&(1<<j))
                    cost[i]=min(cost[i],dp[i|1][j]+G[j][0]);    //合併時需要忽略起點0,這裏處理時忽略起點0

        //求出m花費(解題花費)允許的旅行商可以到達地點的狀態
        cnt=0;
        for(int i=0; i<(1<<n); i++)
        {
            int sum=0;
            for(int j=0; j<n; j++)
                if(i&(1<<j))
                    sum+=point[j].cost;
            if(sum<=m)
                state[cnt++]=i;
        }

        //01揹包求解最少旅行商數和最小路長花費
        memset(dp,0x3f,sizeof dp);
        dp[1][0]=dp[1][1]=0;
        for(int i=0; i<cnt; i++)
            for(int j=(1<<n)-1; j>=0; j--)
                if((j|state[i])==j)
                {
                    dp[j][0]=min(dp[j][0],dp[j^state[i]][0]+1);
                    dp[j][1]=min(dp[j][1],dp[j^state[i]][1]+cost[state[i]]);
                }

        if(dp[(1<<n)-1][0]==INF)
            printf("-1 -1\n");
        else
            printf("%d %d\n",dp[(1<<n)-1][0],dp[(1<<n)-1][1]);
    }
    return 0;
}

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