ZOJ3524:

ZOJ3524

题目:

  • 收集物品,不超过背包的容量,使收集到的价值最多
  • 并且在价值最多的前提下,消耗的能量最小。最后输出消耗最小的能量。
  • 消耗的能量为dist * weight。
  • 每条路只能经过一次,不能再返回。

题解:

  • 两个dp,先dp价值,在价值相同的前提下,dp消耗的能量。
  • 拓扑排序找从s出发的所有路。拓扑排序的特点是结点一层一层的找。所以这一层的结点同时把下一层要更新的结点给更新了。如果有好几个结点到同一个点,每次比较最优的情况,更新。
  • 如果从x到y,x的价值更高,那么从x转移到y;如果价值相同,那么转移最短的距离。如果y价值高,那么就不转移x。然后再进行完全背包问题。

代码:

#include <bits/stdc++.h>
using namespace std;
int const N = 600 + 10;
int const M = 2000 + 10;
int const inf = 0x7f7f7f7f;
int n,m,v,s;
int weight[N],val[N];
int indegree[N],vis[N];
int dp[N][M],dis[N][M];   //dp[i][j]表示在i地点背包装j物品的最大价值,pow[i][j]则表示最小的路程
int maxval,mindis;
struct Edge
{
	int to,dist;	
};
vector<Edge>G[N];
queue<int>seq;
void topo(){
	queue<int>q;
	for(int i=1;i<=n;i++)
		if(indegree[i] == 0)	q.push(i);
	while(!q.empty()){
		int p = q.front();	q.pop();
		seq.push(p);   //储存拓扑序列。
		for(int i=0;i<G[p].size();i++){
			Edge e = G[p][i];
			indegree[e.to]--;
			if(indegree[e.to] == 0)	q.push(e.to);
		}
	}
}	
void solve(){
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;i++)	fill(dis[i],dis[i]+M,inf);
	for(int i=0;i<=v;i++){    //更新起点,后面是从已知点到未知点的更新
		dis[s][i] = 0;   //初始距离为0;
		if(weight[s] <= i)	dp[s][i] = max(dp[s][i],dp[s][i-weight[s]]+val[s]);//dp[s][i-weight[s]]+val[s];
	}
	maxval = dp[s][v],	mindis = 0;  //mindis不用无穷大,原点是最小的
	vis[s] = true;
	while(!seq.empty()){
		int x = seq.front();	seq.pop();
		if(!vis[x])	continue;    //没去过的不能更新下一个
		for(int i=0;i<G[x].size();i++){
			Edge e = G[x][i];
			int y = e.to;
			vis[y] = true;
			for(int j=0;j<=v;j++){    //挑出x和y最优的位置
				if(dp[x][j] > dp[y][j]){     //走到y点
					dp[y][j] = dp[x][j];
					dis[y][j] = dis[x][j] + e.dist * j;
				}else if(dp[x][j] == dp[y][j]){   //价值相同,则考虑最短距离
					dis[y][j] = min(dis[x][j] + e.dist * j,dis[y][j]);    
				}
			}
			for(int j=weight[y];j<=v;j++){   //完全背包转移
				if(dp[y][j] < dp[y][j-weight[y]] + val[y]){
					dp[y][j] = dp[y][j-weight[y]] + val[y];
					dis[y][j] = dis[y][j-weight[y]];
				}else if(dp[y][j] == dp[y][j-weight[y]] + val[y]){
					dis[y][j] = min(dis[y][j],dis[y][j-weight[y]]);		
				}
			}
			for(int j=weight[y];j<=v;j++){    //找最大值
				if(dp[y][j] > maxval){
					maxval = dp[y][j];
					mindis = dis[y][j];
				}else if(dp[y][j] == maxval){
					mindis = min(mindis,dis[y][j]);
				}
			}
		}
	}
}
int main(){
	while(~scanf("%d%d%d%d",&n,&m,&v,&s)){
		for(int i=1;i<=n;i++){
			G[i].clear(),	indegree[i] = 0,	vis[i] = false;
			scanf("%d%d",&weight[i],&val[i]);	//每个旅游景点的商品的质量和价值。
		}
		for(int i=1;i<=m;i++){
			int x,y,l;
			scanf("%d%d%d",&x,&y,&l);  //单向从x到y的路段长度为l
			G[x].push_back((Edge){y,l});
			indegree[y]++;
		}
		topo();
		solve();
		printf("%d\n",mindis);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值