zoj 3524
地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4439
题意:一个有向带权连通图连着n个商店,每个商店里面有一种商品,每个商品有一个体积和价值。有一个背包,当背包里面装m的东西,每走一米消耗m精力,求最多的价值的情况下最少的消耗。
mark:学习大牛的思路http://blog.csdn.net/woshi250hua/article/details/7824773
这题用拓扑排序的好处是,可以确定背包的顺序,因为是图,边很多,直接背包,完全没法做。排好序后,可以每个点先根据父亲节点更新一次,然后再完全背包一次。
dp[i][j]代表第i个节点,背包容量为j时最大的价值,power[i][j]代表最大容量下最小的消耗
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <vector> #include <iostream> using namespace std; struct node { int des,len; }st; const int N = 610; const int M = 2010; int n,m,vmax,x,mm; int v[N], w[N]; vector<node> s[N]; int cnt[N], rd[N], tp[N], flag[N]; //cnt[]是初始化的时候记录每个节点入度 ,tp[]记录排好序后的节点顺序 int dp[N][M],power[N][M]; void topo() { int top = 0; int le = 0; for(int i = 1; i <= n; i++) if(!cnt[i]) rd[++top] = i; while(top) { int aa = rd[top--]; tp[++le] = aa; int l = s[aa].size(); for(int i = 0; i < l; i++) { st = s[aa][i]; cnt[st.des]--; if(!cnt[st.des]) rd[++top] = st.des; } } } void solve() { int i,j,k; int ans = 0; mm = 0; for(i = 0; i <= vmax; i++) { power[x][i] = 0; if(i >= v[x]) dp[x][i] = max(dp[x][i], dp[x][i-v[x]]+w[x]); }
ans = dp[x][vmax]; flag[x] = 1; for(i = 1; i <= n; i++) { int aa = tp[i]; if(!flag[aa]) continue; int l = s[aa].size(); for(j = 0; j < l; j++) { st = s[aa][j]; int bb = st.des; flag[bb] = 1; for(k = 0; k <= vmax; k++) { if(dp[aa][k] > dp[bb][k]) { dp[bb][k] = dp[aa][k]; power[bb][k] = power[aa][k]+st.len*k; } else if(dp[aa][k] == dp[bb][k]) { if(power[bb][k] == -1) power[bb][k] = power[aa][k] + st.len*k; else power[bb][k] = min(power[bb][k], power[aa][k] + st.len*k); } } for(k = v[bb]; k <= vmax; k++) { if(dp[bb][k] < dp[bb][k-v[bb]]+w[bb]) { dp[bb][k] = dp[bb][k-v[bb]]+w[bb]; power[bb][k] = power[bb][k-v[bb]]; } else if(dp[bb][k] == dp[bb][k-v[bb]]+w[bb]) power[bb][k] = min(power[bb][k], power[bb][k-v[bb]]); } for(k = 0; k <= vmax; k++) //这个地方之所以要全部扫面一遍是因为我们做完全背包的时候并不是说恰好达到容量j,而是容量j里面最多的价值,而容量 { //可能根本就没有到j,那么我们通过这个节点去更新下一个节点的时候路程的消耗就会增多,就会出现后面消耗比前面多 if(dp[bb][k] > ans || (dp[bb][k] == ans && mm > power[bb][k])) //但是价值相等的情况。 ans = dp[bb][k], mm = power[bb][k]; } } } } int main() { int i,j,k; while(~scanf("%d%d%d%d", &n, &m, &vmax, &x)) { for(i = 1; i <= n; i++) scanf("%d%d", v+i, w+i); int aa,bb,cc; memset(cnt, 0, sizeof(cnt)); for(i = 0; i <= n; i++) s[i].clear(); for(i = 0; i < m; i++) { scanf("%d%d%d", &aa, &bb, &cc); cnt[bb]++; st.des = bb, st.len = cc; s[aa].push_back(st); } topo(); memset(flag, 0, sizeof(flag)); memset(dp, 0, sizeof(dp)); memset(power, -1, sizeof(power)); solve(); printf("%d\n", mm); } return 0; }