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

发布了23 篇原创文章 · 获赞 21 · 访问量 3287

猜你喜欢

转载自blog.csdn.net/qq_44086097/article/details/104233661