题意:在一个DAG上,主角初始有W钱起点在s点,每个点有一个代价wi和价值vi,主角从起点走到某一点不能回头走,一路上可以买东西(一个点的东西可以买无限次),且体力消耗为身上负重*路径长度。主角可以在任意一点停止旅程,问在获得最大价值情况下体力消耗最小。

解法:第一次遇到这类型的题,看大佬题解,这里说下自己的理解。首先主角在DAG上走且不能回头,那么比较明显让我们想到按拓扑排序顺序进行dp,这是正确的但是代码实现可能不那么容易想:我们首先对DAG进行拓扑排序求出它的拓扑序V,按照拓扑序进行完全背包的DP,这里要注意一点就是因为主角是有起点的,所以有的地方是根本到达不到的,我们可以用个vis数组来代表主角能到达的点,能动到达的点才能DP。DP思路是:设计状态dp[x][j]为走到x点,容量为j的最大价值,同时tp[x][j]为最大价值为dp[x][j]时候的最小体力消耗,那么我们到一个新点,我们先对这个的的物品做完全背包,然后用这个点x去更新x的下一步y的状态(这也是拓扑序DP的核心)。DP的同时更新答案即可。

这里提出一个问题就是为什么在两个限制下要求的答案dp状态不用设计成三维的呢?设计成两位状态能保证是在容量限制下最大价值且在最大价值下的最小消耗吗?这个问题蒟蒻一开始想不通,这是因为答案只要最好的。当然也可以把状态设计成例如dp[x][j][k]代表到x点容量为j价值为k的最小体力消耗,但是注意到因为题目要求的是最大价值下的最小消耗,所以这个状态的除了dp[x][j][Maxv](Maxv是x点容量j的最大价值)是有用的,其他的都是没用的dp[x][j][val]  (val<Maxv)。因为此时容量已经确定,那么以后的状态在这些状态中容量相等的肯定选价值最大的,其他价值小的不会对后面结果造成任何影响。

除此以为这道题还有一些细节,不懂的建议看代码理解:

#include<bits/stdc++.h>
using namespace std;
const int N=+;
const int M=6e4+;
int n,m,W,s,Maxv,Mint,deg[N],w[N],v[N];
vector<int> V; int cnt=,head[N],nxt[M],to[M],len[M];
void add_edge(int x,int y,int z) {
nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
} queue<int> q;
void toposort() {
for (int i=;i<=n;i++) if (deg[i]==) q.push(i);
while (!q.empty()) {
int x=q.front(); q.pop();
V.push_back(x);
for (int i=head[x];i;i=nxt[i]) {
int y=to[i];
if (--deg[y]==) q.push(y);
}
}
} bool vis[N];
int dp[N][],tp[N][];
void solve() {
memset(dp,,sizeof(dp));
memset(tp,0x3f,sizeof(tp));
memset(vis,,sizeof(vis)); vis[s]=;
dp[s][]=; tp[s][]=;
for (int i=;i<V.size();i++) {
if (!vis[V[i]]) continue;
int x=V[i];
for (int j=w[x];j<=W;j++)
if (dp[x][j-w[x]]+v[x]>dp[x][j]) dp[x][j]=dp[x][j-w[x]]+v[x],tp[x][j]=tp[x][j-w[x]];
else if (dp[x][j-w[x]]+v[x]==dp[x][j]) tp[x][j]=min(tp[x][j],tp[x][j-w[x]]);
for (int j=head[x];j;j=nxt[j]) {
int y=to[j];
vis[y]=;
for (int k=;k<=W;k++)
if (dp[x][k]>dp[y][k]) {
dp[y][k]=dp[x][k];
tp[y][k]=tp[x][k]+len[j]*k;
} else if (dp[y][k]==dp[x][k]) {
int tmp=tp[x][k]+len[j]*k;
tp[y][k]=min(tp[y][k],tmp);
}
}
for (int j=;j<=W;j++)
if (dp[x][j]>Maxv || dp[x][j]==Maxv && tp[x][j]<Mint) {
Maxv=dp[x][j]; Mint=tp[x][j];
}
}
} int main()
{
while (scanf("%d%d%d%d",&n,&m,&W,&s)==) {
for (int i=;i<=n;i++) scanf("%d%d",&w[i],&v[i]);
memset(deg,,sizeof(deg));
cnt=; memset(head,,sizeof(head));
for (int i=;i<=m;i++) {
int x,y,z; scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z); deg[y]++;
}
V.clear(); toposort();
Maxv=; Mint=0x3f3f3f3f;
solve();
printf("%d\n",Mint);
}
return ;
}

ZOJ-3524 拓扑排序+完全背包(好题)的更多相关文章

  1. zoj 3524(拓扑排序+多重背包)(好题)

    http://blog.csdn.net/woshi250hua/article/details/7824773 题目大意:从前有n座山,山里都有一座庙,庙里都有一个老和尚,老和尚专送纪念品,每个纪念 ...

  2. Crazy Shopping(拓扑排序+完全背包)

    Crazy Shopping(拓扑排序+完全背包) Because of the 90th anniversary of the Coherent & Cute Patchouli (C.C. ...

  3. ZOJ 4124 拓扑排序+思维dfs

    ZOJ - 4124Median 题目大意:有n个元素,给出m对a>b的关系,问哪个元素可能是第(n+1)/2个元素,可能的元素位置相应输出1,反之输出0 省赛都过去两周了,现在才补这题,这题感 ...

  4. E - Ingredients 拓扑排序+01背包

    题源:https://codeforces.com/gym/101635/attachments 题意: n行,每行给定字符串s1,s2,s3代表一些菜谱名.s2和s3是煮成是的必要条件,然后给出c和 ...

  5. HDU 3342 -- Legal or Not【裸拓扑排序 &amp;&amp;水题 &amp;&amp; 邻接表实现】

    Legal or Not Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tot ...

  6. 第十二届湖南省赛 (B - 有向无环图 )(拓扑排序+思维)好题

    Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始.点 v 结束的路径). 为了方便,点用 1,2,…,n 编号. 设 count(x,y) 表示点 x 到点 y ...

  7. C#LeetCode刷题-拓扑排序

    拓扑排序篇 # 题名 刷题 通过率 难度 207 课程表   40.0% 中等 210 课程表 II   39.8% 中等 329 矩阵中的最长递增路径   31.0% 困难 ​​​​​​​

  8. poj 3687 Labeling Balls(拓扑排序)

    题目:http://poj.org/problem?id=3687题意:n个重量为1~n的球,给定一些编号间的重量比较关系,现在给每个球编号,在符合条件的前提下使得编号小的球重量小.(先保证1号球最轻 ...

  9. [ACM] POJ 3687 Labeling Balls (拓扑排序,反向生成端)

    Labeling Balls Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10161   Accepted: 2810 D ...

随机推荐

  1. python时间日期处理

    一.模块介绍: 1.time模块接近操作系统,模块中的大多数函数是调用了所在平台C library的同名函数,部分函数式平台相关的:基于Unix Timestamp,能表述的日期范围被限定在 1970 ...

  2. STM8硬件设计注意事项

    1.中断 STM8的外部中断和STM32不一样,每个端口PX只有1个中断 2.ADC 1)Additional AIN12 analog input is not selectable in ADC ...

  3. go语言从例子开始之Example14.变参函数

    可变参数函数.可以用任意数量的参数调用.例如,fmt.Println 是一个常见的变参函数. Example: package main import "fmt" //...int ...

  4. 几种RAID级别的比较

    等级 概要 冗余 盘数 读快 写快 RAID 0 便宜.快速.危险 No N Yes Yes RAID 1 高速度.简单.安全 Yes 2(通常) Yes No RAID 5 安全(速度)成本折中 Y ...

  5. Windows下Redis安装+可视化工具Redis Desktop Manager使用

    Redis是有名的NoSql数据库,一般Linux都会默认支持.但在Windows环境中,可能需要手动安装设置才能有效使用.这里就简单介绍一下Windows下Redis服务的安装方法,希望能够帮到你. ...

  6. tp框架基础控制器调用方法

    public function indd(){ //调用该控制器下的某个方法 $this ->index(); //跨控制器调用 $k = A("index");// 创建控 ...

  7. PHP dir() 函数

    使用 dir() 函数: <?php$d = dir(getcwd()); echo "Handle: " . $d->handle . "<br> ...

  8. [CSP-S模拟测试]:抽卡(概率DP)

    题目描述 水上由岐最近在肝手游,游戏里有一个氪金抽卡的活动.有$n$种卡,每种卡有 3 种颜色.每次抽卡可能什么也抽不到,也可能抽到一张卡.每氪金一次可以连抽 m 次卡,其中前$m−1$次抽到第$i$ ...

  9. php把网络图片转Base64编码。(php将图片链接直接转化为base64编码)

    /** 把网络图片图片转成base64 * @param string $img 图片地址 * @return string */ /*网络图片转为base64编码*/ public function ...

  10. js关闭当前页面清除session

    js关闭当前页面清除session 普通页面 <!DOCTYPE html> <html> <head> <meta charset="UTF-8& ...