http://www.luogu.org/problem/show?pid=1941#
我有话说:
这道题类似于完全背包问题。要注意细节问题。设dp(i,j)表示在坐标(i,j)时的最小点击屏幕次数。
状态转移方程:
dp(i,j)=min{dp(i-1,j-x[i-1])+1,dp(i,j-x[i-1])+1,dp(i-1,j+y[i-1])};
#include<iostream>
#include<string>
#include<cstring>
#include<sstream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int maxn=10000+10;
const int maxm=1000+10;
const int INF=100000000;
const int mod=10007;
int f[maxn][maxm];
int x[maxn],y[maxn],down[maxn],up[maxn];
int main()
{
int n,m,a;
cin>>n>>m>>a;
for(int i=0;i<n;i++)scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=n;i++){down[i]=0;up[i]=m+1;}
int p,l,h;
for(int i=0;i<a;i++){
scanf("%d%d%d",&p,&l,&h);
down[p]=l;up[p]=h;
}
//for(int i=0;i<=n;i++)f[i][0]=INF;
//for(int j=1;j<=m;j++)f[n][j]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)f[i][j]=INF;//边界
f[0][0]=INF;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j>=x[i-1]){
f[i][j]=min(f[i][j],f[i-1][j-x[i-1]]+1);
f[i][j]=min(f[i][j],f[i][j-x[i-1]]+1);//连续点击
}
if(j==m){
for(int k=j-x[i-1];k<=m;k++){//对于超过上边界的特殊处理。
f[i][j]=min(f[i][j],f[i-1][k]+1);
f[i][j]=min(f[i][j],f[i][k]+1);
}
}
}
for(int j=down[i]+1;j<=up[i]-1;j++)if(j+y[i-1]<=m)
f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);//在dp(i-1,j+y[i-1])选择下落
for(int j=1;j<=down[i];j++)f[i][j]=INF;
for(int j=up[i];j<=m;j++)f[i][j]=INF;
}
/*for(int j=m;j>=0;j--){
for(int i=0;i<n;i++)
printf("%d ",f[i][j]==INF?9:f[i][j]);
printf("\n");
}*/
int ans=INF,cnt=a;
for(int i=n;i>=1;i--){//检验是否能到达
for(int j=down[i]+1;j<up[i];j++)
if(f[i][j]<INF)
ans=min(ans,f[i][j]);
if(ans!=INF)break;
if(up[i]<=m)cnt--;
}
if(cnt==a)printf("1\n%d\n",ans);
else printf("0\n%d\n",cnt);
return 0;
}