hdu 4284 Travel 2012天津网络赛 状压dp TSP 最短路

题目

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4284

题目来源:天津网络赛,属于第四五题的样子

简要题意:有 N 个城市,M条有权路,开始一个人有 K 块钱,他要逛其中的H个城市,他的钱可能不够,但在这 H 个城市中他可以花Di块买执照并且打工得到 Ci 块,他想要从 1 号城市除法得到所有的执照并回到1号点,问可不可能。

数据范围: N100;M5000;K105every weighted edge (u,v)1u,vN;w105H15;Ci,Di105

题解

读完题目就可以发现这是非常经典的TSP问题,也就是旅行商问题。 H 的范围也正好足够。

实际上我们只需要考虑那H就行了,由于需要知道它们之间的最短路,我们可以Floyd先预处理出任意两点间的距离。

TSP问题常用的处理方法是随机算法和状态压缩dp,此处为求出正确的解我们需要使用状压dp。

考虑 dp[i][j] 为停留在第 i 个点旅行状态为j的最大剩余钱数, j<1K ,第 i 位是否经过了第i个点( 0 开始)。

初始化的时方程式如下:

Remain=Kdis[1][Noi]Di

dp[i][1i]={Remain+Ci          Remain0                           else
状态转移方程式如下:
Remain=dp[from][s xor(1to))]dis[Nofrom][Noto]Dto
dp[to][s]={Remain+Cto            Remain0to is not visited                              else
转移的条件需要买了执照还剩下钱,这点需要注意。

最终的结果为:

Ans=mini=0H1(dp[i][(1H)1]dis[Noi][1])
判断最终 Ans 是否大于等于 0

实现

注意点写即可。比较坑爹的是我一直以为Floyd循环顺序是无关的,结果就跪了很久,其实中间节点要在最外层。。。复杂度Θ(N3+H2H)

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {
    LL res=1;
    a%=MOD;
    for(; b; b>>=1) {
        if(b&1)res=res*a%MOD;
        a=a*a%MOD;
    }
    return res;
}
// head
const int INF = 0x3f3f3f3f;
const int N = 1<<15;
int dp[20][N];
int dis[105][105];

struct Node {
    int no, c, d;
};

Node a[20];
int t, n, m, h, u, v, c, k;
void floyd(int n) {
    for (int i = 1; i <= n; i++) {
        dis[i][i] = 0;
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                dis[i][j] = min(dis[i][k]+dis[k][j], dis[i][j]);
            }
        }
    }
}
int dfs(int x, int s) {
    if (dp[x][s] != -1) return dp[x][s];
    int ans = -1;
    for (int i = 0; i < h; i++) {
        if (((1<<i)&s) && i != x) {
            int temp = dfs(i, s^(1<<x)), rem = temp-dis[a[x].no][a[i].no]-a[x].d;
            if (rem >= 0) {
                ans = max(ans, rem+a[x].c);
            }
        }
    }
    return dp[x][s] = ans < 0 ? -2 : ans;
}
int main() {
    scanf("%d", &t);
    while (t--) {
        memset(dis, INF, sizeof dis);
        memset(dp, -1, sizeof dp);
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &u, &v, &c);
            dis[u][v] = dis[v][u] = min(c, dis[u][v]);
        }
        floyd(n);

        scanf("%d", &h);
        for (int i = 0; i < h; i++) {
            scanf("%d%d%d", &a[i].no, &a[i].c, &a[i].d);
            if (k-dis[1][a[i].no]-a[i].d >= 0) {
                dp[i][1<<i] = k-dis[1][a[i].no]-a[i].d+a[i].c;
            }
        }

        int ans = -1;
        for (int i = 0; i < h; i++) {
            ans = max(dfs(i, (1<<h)-1)-dis[1][a[i].no], ans);
        }
        puts(ans < 0 ? "NO" : "YES");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值