Codeforces Round #744 (Div. 3) G题题解

淦,最后一道题没写出来,...还是我太菜了,不过这个题确实比较有趣.

G. Minimal Coverage

简化题意:就是你处在坐标轴的0点上,给你一个序列(a_i),每次你可以选择向左走(a_i),也可以选择向右走(a_i),求你走过的范围的最小值?(第一步必须向右走)。
其实这个题想起来也没那么复杂,显然每一步在选择时,我们需要一下的信息:当前所处的位置,当前走到哪一步了,当前所走过的范围的左右边界。信息蛮多的,如果用DP的话,当前走到哪一步了,这个很好解决,剩下的三个信息:当前的位置,走过的范围。这个时候我们再看下数据范围:(nleq 10^4,a_ileq 1000),这(a_i)的范围支持我们最多只能再存一个信息。遇到这种信息多,我们无法储存的时候,一般大的解决方法是观察信息之间有没有关系,能不能通过推到得出另一个信息来减少我们存储的信息量。但是:当前的位置,走过的范围。好像没什么明显的联系。剩下的办法就是根据题目的性质,设计更优的状态来减少我们需要记录的信息。可以思考最优解存在的性质,我们发现这个题起点是确定从零开始的,但其实我们完全可以自己调节起点的位置,这对题目没有什么影响。既然如此,我们可以将最优解的左端点移到原点,这样它所有的移动就都是在整数的坐标下移动了。那我们就规定0为左端点,之后考虑我们若在位置x,则右端点肯定越小越好。则我们设这样的状态f[i][j],即规定0为左端点时前i步后,当前点在j时的最小的右端点。发现这样一定能遍历到最优解。虽然当左端点固定后,起点的位置就不固定了,但在起始状态是我们可以枚举确定的,通过这种方法我们可以减少左端点不固定的麻烦。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=1010,qwq=1e9;
int n,a[N],f[N][M<<2];
//f[i][j]表示前i步,当前在j的最小右端点。 
int main()
{
	//freopen("1.in","r",stdin);
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i) scanf("%d",&a[i]);
		for(int i=1;i<=n;++i)
			for(int j=0;j<=3000;++j) f[i][j]=qwq;
		for(int i=a[1];i<=2000;++i) f[1][i]=i;//初始化起点。 
		int ans=qwq;
		for(int i=2;i<=n;++i)
			for(int j=0;j<=2000;++j) //枚举当前所处的位置。 
			{
				if(j>=a[i]) f[i][j]=max(f[i-1][j-a[i]],j);
				if(j+a[i]<=2000) f[i][j]=min(f[i-1][j+a[i]],f[i][j]);
			}
		for(int j=0;j<=2000;++j) ans=min(ans,f[n][j]);	
		printf("%d
",ans);		
	}	
	return 0;
}
原文地址:https://www.cnblogs.com/gcfer/p/15355637.html