【05NOIP提高组】T2 过河

P1052 过河

题目传送门

思路:
设f[i]为在i点上的最少踩石子数则在前面(i-s)到(i-t)的点都可以改变i点的值,因此我们可以取f[i-s]-f[i-t]之中的最小值,另外如果有石头就加上1,如果没有就不加值,这里我们直接用flag[i]表示该点有无石头(有则为1,无则为0)。
因此我们可以写出状态转移方程式:
f [ i ] = m i n ( f [ i − j ] + f l a g [ i ] ) s < = j < = t f[i]=min(f[i−j]+flag[i]) s<=j<=t f[i]=min(f[ij]+flag[i])s<=j<=t

是不是以为很简单,以为这样就结束了?那f数组要定10^9大,这不就爆内存了–所以…[我就放弃了这道题] [额,可能吗] …因此我们需要根据NOIpN抄原题的优良传统来想,我们看到了17年的小凯的疑惑。

路径压缩:
假设每次走p或者p+1步.我们知道gcd(p,p+1)=1.
由扩展欧几里得可知,对于二元一次方程组:
px+(p+1)y=gcd(p,p+1)是有整数解的,即可得:px+(p+1)y=s是一定有整数解的。
设px+(p+1)y=s的解为:x=x0+(p+1)t,y=y0-pt。令0<=x<=p(通过增减t个p+1来实现),s>p*(p+1)-1,则有: y = s − p x p + 1 > = s − p 2 p + 1 > p ∗ ( p + 1 ) − 1 − p x p + 1 > = 0 y=\frac{s-px}{p+1}>=\frac{s-p^2}{p+1}>\frac{p*(p+1)-1-px}{p+1}>=0 y=p+1spx>=p+1sp2>p+1p(p+1)1px>=0

即表示,当 s>=p*(p+1)时,px+(p+1)y=s 有两个非负整数解,每次走p步或者 p+1 步,p∗(p+1) 之后的地方均能够到达。

如果两个石子之间的距离大于 p*(p+1) ,那么就可以直接将他们之间的距离更改为 p*(p+1) 。

综上,得到压缩路径的方法:若两个石子之间的距离> t∗(t−1) ,则将他们的距离更改为 t*(t-1) 。
因为 t<=10 ,因此我们可以直接将大于10*9的距离直接化为90.
但是要注意,对于 s=t这种特殊情况,这种方法是不成立的应为在这种情况下,每次是不能够走p+1步的,因此需要另外特殊判断。
方程如下:

f[i]=f[i−1]+(i mod s ==0)
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=100000000;
const int N=1e6;
int l, s, t, m, a[1100], flag[1100000], f[1000010], ans, temp;
int main()
{
	scanf("%d%d%d%d", &l, &s, &t, &m);
	for(int i=1; i<=m; i++) scanf("%d", &a[i]), ans+=(a[i]%s == 0);
	if(s == t) {printf("%d", ans); return 0;}
	sort(a+1,a+m+1);
	temp=min(l - a[m], t * (t - 1));
	l=0;
	for(int i=1; i<=m; i++)
	{
		l+=min(a[i] - a[i - 1] , t * (t - 1));
		flag[l]=1;
	}
	l+=temp;
	for(int i=1; i<=l + 9; i++)
	{
		f[i]=MAX;
		for(int j=s; j<=t; j++)
			if(i >= j) f[i]=min(f[i], f[i - j] + flag[i]);	
	} 
	ans=MAX;
	for(int i=l; i<=l + 9; i++) ans=min(ans, f[i]);
	printf("%d", ans);
	return 0;
} 

你可能感兴趣的:(CSP-S提高组,状态压缩)