AcWing 1011. 挖地雷 -- dp
题目
在一个地图上有 \(n\) 个地窖(\(n≤200\)),每个地窖中埋有一定数量的地雷。
同时,给出地窖之间的连接路径,并规定路径都是单向的,且保证都是小序号地窖指向大序号地窖,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。
某人可以从任意一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。
设计一个挖地雷的方案,使他能挖到最多的地雷。
注意:数据保证挖到最多地雷的路线只有一条。
输入格式
第一行包含整数n,表示地窖的个数。
第二行包含n个整数,依次为每个地窖地雷的个数。
接下来若干行,每行包含两个整数\(x_i,y_i\),表示从\(x_i\)可到\(y_i\),\(x_i<y_i\)。
最后一行为”0 0”表示结束。
输出格式
第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个‘-’分隔,形如:\(k_1−k_2−…−k_v\) 。
第二行包含一个整数,表示能挖到的最多地雷数。
输入样例:
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个地窖结尾所挖到的地雷最大值
//思路对了 但代码还是没写出来
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int n;
int res;
int pos; //记录挖到最大地雷数的最后一个地窖编号
int g[N][N], w[N];
int f[N];
int p[N]; //我们利用求祖宗节点的方式 输出挖地雷的路径 用数组存一下每个数的祖宗节点
void find(int x) //找挖到最大地雷数路径上的地窖编号 输入的是末尾的地窖编号
{
if(p[x]) find(p[x]); //如果x有前驱节点 继续搜 直到搜到祖宗节点 祖宗节点的p[]=0;然后就可以向下执行 ;执行后由于p[祖宗]==0,函数不断返回0,从而可以输出路径上所有地窖编号
if (p[x] == 0) printf("%d", x);
else printf("-%d", x); //输出当前节点
}
int main()
{
// ios::sync_with_stdio(false); //本题输入输出流取消关联后还会影响答案输出 神奇 要把最后的代码改成这样才能重开一行printf("\n%d\n", res);
// cin.tie(0);
// cout.tie(0);
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
int x, y;
while(cin >> x >> y, x && y) g[x][y] = 1; // 建立拓扑图;
for (int i = 1; i <= n; i ++ ) f[i] = w[i]; //初始化f[i]初始值是其自身能挖到的地雷个数
for (int i = 2; i <= n; i ++ ) //因为地窖1不能作为结尾的点,所以从第2个点遍历到n号地窖
{
for (int j = i; j > 0; j -- ) //能在i号点前的地窖肯定编号比i小
{
if (g[j][i] == 1 && f[j] + w[i] > f[i]) //判断当前从j号地窖到i号地窖有没有路径且是否大于当前最大值
{
f[i] = f[j] + w[i];
//cout << f[i] << endl;
p[i] = j; //记录路径的关键一步!记录此时路径i的上一个节点是j
}
}
if (res < f[i]) //每次记录完以i结尾的路径产生的地雷最大值后,与res比较,如果当前地雷数更大
{
res = f[i];
pos = i; //记录当前结尾地窖编号i
}
}
find(pos);
//cout << endl;
printf("\n%d\n", res);
//cout << f[2] << endl;
return 0;
}