**HDU 4281 - Judges' response(DP`背包+TSP)

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=4281

题意:

给出n个题目的坐标和花费的时间,每个裁判解决题目的时间为m,每个题目只能让一个裁判解决。

求出1.最少派出多少裁判才能将所有的题目解决。2.不限裁判数量,解决所有的问题的最少路径。

思路:

解决问题一:

将2^n种的地点选择 看成 2^n种物品,物品的权值为所选择的物品的总权值,将合法的总权值(<= m)存起来,物品总数为tot。dp【i】表示权值为i时最少的裁判数。

dp[i] = min(dp[i], dp[j]+1)..

解决问题2:

这是一个多旅行商问题。

思路是将mtsp转化成tsp,然后将各个tsp合并成答案。

先预处理np[i]: 表示一个裁判走的集合为i德尔所有地点且回到原点的最少权值和。

np[i] = min(np[i], np[j|1] + np[(i-j)|1]) ..

AC.

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 17;
int n, m;
int x[maxn], y[maxn], v[maxn];
int tot, state[1<<maxn], sta[1<<maxn];
int dis[maxn][maxn];


bool ok(int x)
{
    int sum = 0;
    for(int i = 0; i < n; ++i) {
        if(x & (1<<i)) sum += v[i];
    }
    return sum <= m;
}

int cal(int a, int b)
{
    return ceil(sqrt((x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b])*(y[a]-y[b])));
}
int cal_dis()
{
    for(int i = 0; i < n; ++i) {
        for(int j = i+1; j < n; ++j) {
            dis[i][j] = dis[j][i] = cal(i, j);
        }
    }
}

int dp[1<<maxn];
int solve1()
{
    fill(dp, dp+(1<<maxn)+1, inf);
    dp[0] = 0;
    for(int i = 0; i < tot; ++i) {
        for(int j = (1<<n)-1; j >= 0; --j) {
            if(dp[j] == inf) continue;
            int c = j + state[i];
            if(c != (j | state[i])) continue;
            dp[c] = min(dp[c], dp[j] + 1);
        }
    }
    return dp[(1<<n)-1] == inf? -1: dp[(1<<n)-1];
}

int cost[maxn][(1<<maxn)], np[(1<<maxn)];
int solve2()
{
    for(int i = 0; i < n; ++i) {
        fill(cost[i], cost[i] + (1<<n), inf);
    }
    cost[0][1] = 0;
    fill(np, np+(1<<n), inf);

    for(int i = 1; i < (1<<n); ++i) {
        if(sta[i]) {
            for(int j = 0; j < n; ++j) {
                if(i & (1<<j)) {
                    np[i] = min(np[i], cost[j][i] + dis[j][0]);
                }
                for(int k = 0; k < n; ++k) {
                    if((i & (1<<k)) == 0) {
                        cost[k][i|(1<<k)] = min(cost[k][i|(1<<k)], cost[j][i] + dis[j][k]);
                    }
                }
            }
        }
    }

    for(int i = 1; i < (1<<n); ++i) {
        if(i&1) {
            for(int j = (i-1)&i; j; j = (j-1)&i) { //j为i的二进制子集。
               // printf("i = %d, j = %d\n", i, j);
                np[i] = min(np[i], np[j|1] + np[(i-j)|1]);
            }
        }
    }
    return np[(1<<n)-1] == inf? -1: np[(1<<n)-1];
}

int main()
{
    freopen("in", "r", stdin);
    while(~scanf("%d %d", &n, &m)) {
        for(int i = 0; i < n; ++i) {
            scanf("%d %d", &x[i], &y[i]);
        }
        for(int i = 0; i < n; ++i) {
            scanf("%d", &v[i]);
        }

        tot = 0;
        for(int i = (1<<n); i >= 0; --i) {
            sta[i] = ok(i);
            if(sta[i]) state[tot++] = i;
        }

        memset(dis, 0, sizeof(dis));
        cal_dis();

        int ans1 = solve1();
        int ans2 = solve2();

        printf("%d %d\n", ans1, ans2);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值