NOIP2014飞扬的小鸟

好久没写博客啦。发几篇最近做过的题。

这道题显然是背包,选择跳几次或不跳。暴力打背包是70分。
如果填表法:我们发现转移的时候如果不考虑下降,那么上升的可以同列转移
(f【i】【j】转移到f【i】【j+k】)所以先转移上升的再转移下降的,最后填充非法情况。

对于刷表法:会刷到重复的状态,如果值不优,就停止刷表,因为一定不会更优了。

最后WA的原因——没处理好越界后高度维持上限的问题。

int gg=(m-j)/up[i]+1;
for(int k=1;k<=gg;k++){
int md=min(m,j+k*up[i]);//比较精髓的处理方式,上限和枚举的取最小值。
#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e3+5;

int f[MAXN*10][MAXN];//f[i][j]表示横坐标i,纵坐标j最小点击次数
int n,m,k;//长度,高度,水管数。 

int up[MAXN*10],down[MAXN*10];
int limitup[MAXN*10],limitdown[MAXN*10],ansnum=0;
bool limit[MAXN*10],vis[MAXN*10];

void mem(int n){
    memset(f,0x3f,sizeof(f));
    memset(limit,0,sizeof(limit));
    memset(vis,0,sizeof(vis));
    memset(limitup,0,sizeof(limitup));
    memset(limitdown,0,sizeof(limitdown));
    for(int i=0;i<=n;i++)f[0][i]=0;
} 

int main(){
    scanf("%d%d%d",&n,&m,&k);
    mem(m);
    for(int i=0;i<n;i++){
        scanf("%d%d",&up[i],&down[i]);
    }
    for(int i=0;i<k;i++){
        int tem1,tem2,tem3;
        scanf("%d%d%d",&tem1,&tem2,&tem3);
        limit[tem1]=1;
        limitdown[tem1]=tem2;
        limitup[tem1]=tem3;
    }
    int tem1=0;
    for(int i=0;i<n;i++){
        for(int j=m;j>=0;j--){//当前状态的高度 
            if(limit[i]&&j<=limitdown[i])continue;
            else if(limit[i]&&j>=limitup[i])continue;
            else if(f[i][j]==1061109567)continue;
            if(limit[i]&&!vis[i]){
                vis[i]=1;ansnum++;  
            }
            int gg=(m-j)/up[i]+1;
            for(int k=1;k<=gg;k++){
                int md=min(m,j+k*up[i]);
                if((limit[i+1]&&j+k*up[i]>limitdown[i+1]&&j+k*up[i]<limitup[i+1])||!limit[i+1])
                if(f[i][j]+k<f[i+1][md])f[i+1][md]=f[i][j]+k;
                else break;
            }
            if(j-down[i]>0){
            //  cout<<f[i][j]<<endl;
            //  if(!i)cout<<j<<endl;
                //if(i==0)cout<<j-down[i]<<endl;
                if((limit[i+1]&&j-down[i]>limitdown[i+1]&&j-down[i]<limitup[i+1])||!limit[i+1])f[i+1][j-down[i]]=min(f[i+1][j-down[i]],f[i][j]);
            }
        }
    }
    int ans=1061109567;
    for(int i=0;i<=m;i++)ans=min(ans,f[n][i]);
    if(ans!=1061109567)printf("1\n%d\n",ans);
    else printf("0\n%d\n",ansnum);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值