题目
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4284
题目来源:天津网络赛,属于第四五题的样子
简要题意:有 N 个城市,
M 条有权路,开始一个人有 K 块钱,他要逛其中的H 个城市,他的钱可能不够,但在这 H 个城市中他可以花Di 块买执照并且打工得到 Ci 块,他想要从 1 号城市除法得到所有的执照并回到1 号点,问可不可能。数据范围: N⩽100;M⩽5000;K⩽105every weighted edge (u,v)1⩽u,v⩽N;w⩽105H⩽15;Ci,Di⩽105
题解
读完题目就可以发现这是非常经典的TSP问题,也就是旅行商问题。 H 的范围也正好足够。
实际上我们只需要考虑那
H 就行了,由于需要知道它们之间的最短路,我们可以Floyd先预处理出任意两点间的距离。TSP问题常用的处理方法是随机算法和状态压缩dp,此处为求出正确的解我们需要使用状压dp。
考虑 dp[i][j] 为停留在第 i 个点旅行状态为
j 的最大剩余钱数, j<1≪K ,第 i 位是否经过了第i 个点( 0 开始)。初始化的时方程式如下:
Remain=K−dis[1][Noi]−Di dp[i][1≪i]={Remain+Ci Remain⩾0−∞ else状态转移方程式如下:
Remain=dp[from][s xor(1≪to))]−dis[Nofrom][Noto]−Dtodp[to][s]={Remain+Cto Remain⩾0∧to is not visited−∞ else转移的条件需要买了执照还剩下钱,这点需要注意。最终的结果为:
Ans=mini=0H−1(dp[i][(1≪H)−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;
}