【NOIP2014】飞扬的小鸟


设 f[i][j]表示在坐标(i,j)点时的最小点击屏幕次数。

DP方程为 f[i][j]=min(f[i-1][j-x[i-1]]+1,f[i][j-x[i-1]]+1,f[i-1][j+y[i-1]]);

分别表示点击一次,点击多次和下降的情况。

实现方案如下:

    首先全部赋最大值

    然后处理一次点击和连续点击,注意到了上界需要特殊的搞一下

    再处理下落的情况

    处理完后记得把柱子的f赋为最大值

     然后最后扫一遍判断答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cctype>
#include<cfloat>
#include<vector>
#include<map>
using namespace std;
const int maxn=10000+10;
const int maxm=1000+10;
const int INF=100000000;
int f[maxn][maxm];
int x[maxn],y[maxn],down[maxn],up[maxn];
int main()
{
  freopen("!.in","r",stdin);
  freopen("!.out","w",stdout);
  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=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);
	}
      for(int j=m-x[i-1];j<=m;j++)
	{
	  f[i][m]=min(f[i][m],f[i-1][j]+1);
	  f[i][m]=min(f[i][m],f[i][j]+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]]);
      for(int j=1;j<=down[i];j++) f[i][j]=INF;
      for(int j=up[i];j<=m;j++) f[i][j]=INF;
    }
  int ans=INF,num=a;
  for(int i=n;i>=1;i--)
    {
      for(int j=down[i]+1;j<up[i];j++)
	ans=min(ans,f[i][j]);
      if(ans!=INF) break;
      if(up[i]<=m) num--;
    }
  if(num==a) printf("1\n%d",ans);
  else printf("0\n%d",num);
  return 0;
}


 

posted @ 2017-02-08 22:09  嘘丶  阅读(135)  评论(0编辑  收藏  举报