【洛谷P1052】过河 离散化+dp

题目大意:给定一个长度为 N 的序列,有 M 个点对答案的贡献为 1,其余为 0,现从起点出发,每次只能走 [s,t] 个单位,求从起点走到终点时答案贡献最小是多少。

题解:由于 N 很大,无法直接记录状态。观察发现 M 很小,且 [s,t] 也很小,因此,考虑到只有在答案贡献为 1 的点的附近 dp 值才可能会发生变化,其余位置会导致大量的解的重复而浪费时间和空间。基于以上想法,考虑缩点,即:对于两个石子之间的距离来说,是否存在一个点 s0 使得当 s>s0 时,无论从前一个石子之前的任何位置,通过任何步数的组合,均可以到达 s ,则从 s0 到下一个石子的位置之前的 0 贡献部分可以被缩掉。这样,根据裴蜀定理可得,对于 \(len>t(t-1)\) 时,即可满足上述条件。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;

int n,s,t,m,l,pos[101];
int dp[maxn];
bool is[maxn];

void read_and_parse(){
	scanf("%d%d%d%d",&n,&s,&t,&m);
	for(int i=1;i<=m;i++)scanf("%d",&pos[i]);
}

void solve(){
	if(s==t){
		int ans=0;
		for(int i=1;i<=m;i++)if(pos[i]%s==0)++ans;
		return (void)printf("%d\n",ans);
	}
	sort(pos+1,pos+m+1);
	for(int i=1;i<=m;i++)l+=min(pos[i]-pos[i-1],100),is[l]=1;
	l+=min(n-pos[m],100);
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<=l+9;i++)
		for(int j=s;j<=t;j++)if(i>=j)
			dp[i]=min(dp[i],dp[i-j]+is[i]);
	int ans=1e9;
	for(int i=l;i<=l+9;i++)ans=min(ans,dp[i]);
	printf("%d\n",ans);
}

int main(){
	read_and_parse();
	solve();
	return 0;	
} 
posted @ 2019-03-25 21:07  shellpicker  阅读(167)  评论(0编辑  收藏  举报