好久没写博客啦。发几篇最近做过的题。
这道题显然是背包,选择跳几次或不跳。暴力打背包是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;
}