挖地雷——线性dp
题目描述
在一个地图上有N
个地窖(N<=200
),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。
某人可以从任一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使他能挖到最多的地雷。
输入格式
第一行一个整数n
表示有n
个地窖
第二行有n
个整数表示每个地窖的地雷数
以下有若干行,每行有两个数x,y
表示x
可以到y
,保证x
小于y
。
最后一行有两个0
,表示输入结束
输出格式
第一行输出挖地雷的顺序。
第二行为最多挖出的地雷数
样例
样例输入
6
5 10 20 5 4 5
1 2
1 4
2 4
3 4
4 5
4 6
5 6
0 0
样例输出
3-4-5-6
34
明确:地道连成的图是一个 有向无环图 。我们要把 到达每一个地窖中能得到的最优答案(即到达该地窖时能挖到的最多地雷数) 求出来即可。
用 f[i] 表示到达 i 号地窖时能挖到的最多地雷数,a[i] 表示每个地窖里的地雷数。
那么就有 f[i]=max(f[j]+a[i]); 这里面的 j 表示能到达 i 的地窖编号,即 i 的父亲。
对于路径:用 p[i] 存储从哪里到达 i 。也就是说,最后的路径中,想到达 i ,要先到达 p[i];想到达 p[i],要先到达 p[p[i]].....显然这是要递归输出的。当 p[i]==0 时,说明没有地窖能到达 i,那么此时 i 就是要开始挖的地窖编号。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> const int MAXN = 200+3; using namespace std; int n, a[MAXN], f[MAXN], p[MAXN], ans, last; bool fg[MAXN][MAXN];//存储通路:f[i][j]表示能否从i到j void Print(int x){ if(x==0) return;//如果父亲节点为0,则递归返回 Print(p[x]);//递归x的父亲节点之后再输出x printf("%d-",x); return; } int main(){ ans = -1;//初始为负数 scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); f[i]=a[i];//初始化f:没连通时,最优值只有自己的价值 } int x, y; while(scanf("%d%d",&x,&y) != EOF){ fg[x][y] = true; } for(int i=1;i<=n;i++){//枚举终点 for(int j=1;j<=n;j++){//枚举起点 if(fg[j][i] && f[i]<f[j]+a[i]){//如果能从j到i,且能获得更大价值 f[i]=f[j]+a[i];//更新f[i] p[i]=j;//记录 最后 更新的 i的父亲(最后更新的为最优) } } if(ans<f[i]){ ans = f[i]; last = i;//last 存储最后更新的节点编号,保存递归输出的起点 } } Print(p[last]);//为了能够处理连字符的问题,我们把最后一个点单独输出 printf("%d\n%d",last,ans); return 0; }