题目传送门

题目描述

查看题解 查看答案

题目描述

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

时间复杂度

参考文章

posted @ 2022-03-11 10:46  VanHope  阅读(45)  评论(0编辑  收藏  举报