目录

USACO 2020 Jan Gold P1

USACO analysis

李若谷 | 14 Apr 2021

题目大意

N个点M条边的有向图,每个点都有一个值$m_i$。求从点1出发回到点1的m的总和减去$sum^2$(走过的边数)*c(一个给定的常数)的最大值

思路

$dp(i,j)$代表从1出发,经过了i条边,到点j的m总和的最大值。 可以得出$dp(i,j) = \max$$(dp(i-1,k)) +m_j$ 其中k是所有能直接到j的点。 那么答案就是$\max dp(i,1) - i^2*c$ i也就是经过的边数是不确定的。 但是可以看出,边数最大的情况就是c为1, $m_i = 1000$(题目给出的m的最大值)的图。 这样的图可以得到方程$f(x) = 1000x - x^2$ 其中x代表走过的边数,f(x)代表经过x条边后的值,由于图是固定的这个值也是固定的。 在这个方程中若要让f(x)>=0, x取的最大值是1000,所以最多经过1000条边。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1005;
const int maxm = 2005;
int c,n,m;
int money[maxn];
int ans = 0;
inline int read()
{
	int sum=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) 
		sum=sum*10+(ch^48),ch=getchar();
	return sum;
}
struct Edge{
	int to;
	int next;
}edge[maxm];
int head[maxn];
int cnt = 0;
int dp[1005][maxn];
void add_edge(int x,int y)
{
	edge[++cnt].to = y;
	edge[cnt].next = head[x];
	head[x] = cnt;
}
int main()
{	
	n=read();
	m=read();
	c=read();
	for(int i=1;i<=n;i++){
		scanf("%d",&money[i]);
	} 
	for(int i=1;i<=m;i++)
	{
		int x,y;
		x=read();
		y=read();
		add_edge(y,x);
	}
	memset(dp,-1,sizeof(dp));
	dp[0][1] = 0;
	for(int i=1;i<=1000;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=head[j];k;k=edge[k].next){
				int las = edge[k].to;
				if(dp[i-1][las]!=-1)
					dp[i][j] = max(dp[i][j],dp[i-1][las] + money[j]);
			}
		}
		ans = max(ans,dp[i][1]-c*i*i);

	}
	cout<<ans<<endl;
	return 0;	
}

时间复杂度

$O(n^2)$


评论区