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; }

转载于:https://www.cnblogs.com/andre0506/archive/2012/09/25/2702136.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值