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