ZOJ 3524 Crazy Shopping DAG上的完全背包

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4439

题意:

n个点的有向无环图,边有长度,从一个点到另一点消耗背包重量与边长的乘积。每个点卖物品,价值v,重量w,数量无限,给定起点,和背包容量,可以在任意点停止,问最大化所获得价值的情况下需要消耗的最少能量。

分析:

前两天一拙计没想出来,先放着了,今天水了几次才过= =。f[i][j]表示在i点用j的容量所得最大价值,g[i][j]表示同样状态下获得最大价值所需最小能量,然后拓扑排序,转移即可。因为题是前两天读的,今天想了方程就写,忘记考虑图的连通性,如果不能由起点走到的点就不能用它的答案来更新解。

感想:

前两天卡住主要是突然想不清状态转移。实际上像这题,状态转移可以分开,即同时进行顺推和逆推,循环到i,j的状态时,既是求i,j的状态,也是将i,j的状态转移给所能到的点to,j。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #include<queue>
 5 using namespace std;
 6 
 7 typedef pair<int, int> edge;
 8 int n, m, w, x, st;
 9 vector<edge> e[610];
10 long long f[610][2100], g[610][2100];
11 int c[610], v[610], bfn[610], in[610];
12 bool reach[610];
13 int main()
14 {
15     while(scanf("%d %d %d %d", &n, &m, &w, &x) != EOF)
16     {
17         for (int i = 1; i <= n; i++){
18             scanf("%d %d", c+i, v+i); 
19             e[i].clear();
20         }
21         memset(in, 0, sizeof(in));
22         int xx, yy, ww;
23         for (int i = 0; i < m; i++)
24         {
25             scanf("%d %d %d", &xx, &yy, &ww);
26             e[xx].push_back(make_pair(yy, ww));
27             in[yy] ++;
28         }
29         queue<int> q;
30         for (int i = 1; i <= n; i++)
31             if (in[i] == 0) q.push(i);
32         int cnt = 0;
33         while(!q.empty())
34         {
35             int u = q.front(); q.pop();
36             bfn[++cnt] = u;
37             if (u == x) st = cnt;
38             for (int i = 0; i < e[u].size(); i++){
39                 int v = e[u][i].first;
40                 in[v] --;
41                 if (!in[v]) q.push(v);
42             }
43         }
44         memset(reach, false, sizeof(reach));
45         reach[x] = true;
46         long long ans = 1 << 30, maxv = 0;
47         ans <<= 30;
48         memset(f, 0, sizeof(f));
49         for (int mark = st; mark <= n; mark++){
50             int i = bfn[mark];
51             if (!reach[i]) continue;
52             for (int j = 0; j <= w; j++){
53                 if (j >= c[i]){
54                     long long tmp = f[i][j-c[i]] + v[i]; 
55                     if (f[i][j] < tmp){
56                         f[i][j] = tmp;
57                         g[i][j] = g[i][j-c[i]];
58                     }
59                     else if (f[i][j] == tmp){
60                         g[i][j] = min(g[i][j], g[i][j-c[i]]);
61                     }
62                 }
63                 for (int t = 0; t < e[i].size(); t++){
64                     int v = e[i][t].first, l = e[i][t].second;      //v肯定还没遍历过
65                     reach[v] = true;                     //f[v][j]暂时表示v点j的容量全由之前点
66                     if (f[i][j] > f[v][j]){                 //消耗所能获得最大价值
67                         f[v][j] = f[i][j];
68                         g[v][j] = g[i][j] + l * j;
69                     }
70                     else if (f[i][j] == f[v][j]){
71                         g[v][j] = min(g[v][j], g[i][j] + l * j);
72                     }
73                 }
74                 if (f[i][j] > maxv){
75                     maxv = f[i][j];
76                     ans = g[i][j];
77                 }
78                 else if (f[i][j] == maxv) ans = min(ans, g[i][j]);
79             }
80         }
81         printf("%lld\n", ans);
82     }
83     return 0;
84 }

 

posted @ 2014-08-06 20:38  james47  阅读(360)  评论(0编辑  收藏  举报