正题
题目链接:https://www.luogu.org/problemnew/show/P1941
题目大意
n∗mn*mn∗m的场地,一只鸟,在iii格点击一次升XiX_iXi格(可以点击多次),不点击掉YiY_iYi格。不能落地,最高mmm格,然后不能撞到地图上的一些管子。
求是否能够过关,如果能输出最少点击次数,否则输出能过多少个管子。
解题思路
设fi,jf_{i,j}fi,j表示到(i,j)(i,j)(i,j)位置时,最少点击次数。然后转移
首先是掉落
fi+1,j−downi+1=fi,jf_{i+1,j-down_{i+1}}=f_{i,j}fi+1,j−downi+1=fi,j
然后考虑上升,因为可以点击多次,我们可以用多重背包的方法
fi+1,max{j+upi+1,m}=max{fi,j,fi+1,j}f_{i+1,max\{j+up_{i+1},m\}}=max\{f_{i,j},f_{i+1,j}\}fi+1,max{j+upi+1,m}=max{fi,j,fi+1,j}
之后将管子位置的fi,jf_{i,j}fi,j赋值为infinfinf就不会影响到后面的转移了。
codecodecode
#include<cstdio>
#include<algorithm>
#include<cstring>
#define upa min(j+up[i+1],m)
using namespace std;
const int N=11000,M=1100;
struct node{int x,down,up;
}g[N];
int n,m,k,l,ans,mins=2147483647;
int up[N],down[N],f[N][M];
bool cmp(node x,node y)
{return x.x<y.x;}
int main()
{scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=n;i++)scanf("%d%d",&up[i],&down[i]);for(int i=1;i<=k;i++)scanf("%d%d%d",&g[i].x,&g[i].down,&g[i].up);sort(g+1,g+1+k,cmp);l=1;memset(f,0x3f,sizeof(f));memset(f[0],0,sizeof(f[0]));for(int i=0;i<=n;i++){if(g[l].x==i&&l<=k){for(int j=1;j<=g[l].down;j++)f[i][j]=1061109567;for(int j=g[l].up;j<=m;j++)f[i][j]=1061109567;bool flag=1;for(int j=1;j<=m;j++)if(f[i][j]<1061109567){flag=0;break;}if(flag){printf("0\n%d",l-1);return 0;}l++;}for(int j=1;j<=m;j++)f[i+1][upa]=min(min(f[i][j],f[i+1][j])+1,f[i+1][upa]);for(int j=down[i+1]+1;j<=m;j++)f[i+1][j-down[i+1]]=min(f[i+1][j-down[i+1]],f[i][j]);}for(int j=1;j<=m;j++)mins=min(mins,f[n][j]);printf("1\n%d",mins);
}