图
题目描述
图
题目描述
Time Limit: 1000 ms
Memory Limit: 256 mb给定一个有 n 个点,m 条边的有向图。图中第 i 个点的价值是 vi,每条边有一个代价 z,不同的边代价可能不一样。
一共有 q 个询问,每次询问包含两个数字 u,c,表示询问从 u 点出发,经过代价总和不超过 c 的边所能到达的点的价值总和的最大值。
如果一个点被多次经过,那么其价值要计算多次。初始节点的价值也要计算进去。
输入输出格式
输入描述:
从标准输入读入数据。 输入的第一行包含三个由空格隔开的正整数 n,m,q,保证 N≤2,000 和 M≤8,000,Q≤10^5。 接下来的一行包括 n 个由空格隔开的非负整数 vi 表示编号从小到大所有点的价值,保证 vi≤10^4。 接下来的 m 行每行包含三个由空格隔开的正整数 x,y,z,保证 1≤x,y≤n 和 1≤z≤30,表示存在一条从 x 到 y 代价为 z 的有向边。 接下来的 q 行每行包含两个由空格隔开的非负整数 u,c,保证 1≤u≤n 和 0≤c≤800。
输出描述:
输出到标准输出。 对于每次询问输出一个数,表示相应的答案。
输入输出样例
输入样例#:
复制
4 4 2 3 2 3 4 1 2 1 2 3 1 3 2 2 3 4 1 2 6 3 2
输出样例#:
复制
14 7
提示
对于第一个询问最优方案是从 2 出发,经过 3,2,3,4 四个点,取得的价值是 2+3+2+3+4=14。 对于第二个询问最优方案是从 3 出发,经过 4 这个点,取得的价值是 3+4=7。
题目来源
清华大学2020年机试题
dfs + 记忆化
分析
错误分析:
有个错误让我搞了两天
就是在读取 x y z的时候,因为是用邻接矩阵存的边,我一开始写的是scanf("%d%d%d", &a, &b, &g[a][b]);
但是这样读入是错的!!!!因为要先读a,b,读完a,b之后g[a][b]
里面的ab不一定就赋值好了!!!
应该改成
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
g[a][b] = w;
!!!切记
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2005, C = 810, M = 8010;
int n, m, q;
int g[N][N]; // 邻接矩阵
int val[N]; // 每个点价值
int f[N][C]; // 记忆化数组
int h[N], e[M], ne[M], idx = 0;
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dfs(int u, int c)
{
int &v = f[u][c];
if(v != -1) return v;
int tmp = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
int weg = g[u][j];
//
if(c >= weg)
{
tmp = max(tmp, dfs(j, c-weg));
}
}
//return tmp;
return v = tmp + val[u];
}
int main()
{
memset(h, -1, sizeof h);
memset(f, -1, sizeof f);
scanf("%d%d%d",&n, &m, &q);
for(int i = 1; i <= n; i++) scanf("%d", &val[i]);
while(m--)
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
g[a][b] = w;
add(a, b);
}
while(q--)
{
int u, c;
scanf("%d%d", &u, &c);
printf("%d\n", dfs(u, c));
}
return 0;
}