不得不说出这题的人太强了!!!此题结合了01背包、完全背包以及各种细节!!!
题意:
一张图长n高m,有k个关卡,每个关卡只能从l+1到h-1间通过,小鸟从位置0的任意高度出发,对于每一个位置i,点击一下可以上升xi的高度,每个位置可以点击无数次,但是如果不点击,就会下降yi的高度,达到顶部m不能继续向上但合法,降到底部0为非法,求若能通关的最小点击次数,若不能通关,求能通过的最大关卡数
dp题解:
f[i][j]表示达到i,j位置时的最小点击数,初始时除了f[0][1~m]=0,都为无限大
完全背包:
因为一个位置i可以点击无数次,所以上升的操作就是一个完全背包
f[i][j] = min(f[i - 1][j - x[i]] + 1,f[i][j - x[i]] + 1)
01背包:
因为不操作只能下降y[i],所以下降操作是01背包
f[i][j] = min(f[i][j],f[i - 1][j + y[i]])
细节:
如果达到m,对于m时的最小值,我们还要枚举m~m+x[i]中求最小
给定通过关卡的高度范围为l~h但是由于不能卡着边过,所以范围要处理为l+1~h-1
由于对边界有特殊的判断,所以数组下标范围要比m大一点
时间复杂度:O(nm)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int x[11005],y[11005],high[11005],low[11005],f[11005][2005];//数组要稍微开大一点 6 bool vis[11005]; 7 int main() 8 { 9 // freopen("1.in","r",stdin); 10 int n,m,k; 11 scanf("%d%d%d",&n,&m,&k); 12 for(int i =1;i <= n;i ++) 13 { 14 scanf("%d%d",&x[i],&y[i]); 15 } 16 for(int i = 1;i <= n;i ++) 17 { 18 high[i] = m; 19 low[i] = 1; 20 } 21 for(int i = 1,p,l,h;i <= k;i ++) 22 { 23 scanf("%d%d%d",&p,&l,&h); 24 vis[p] = 1; 25 high[p] = h - 1; 26 low[p] = l + 1; 27 } 28 memset(f,0x3f,sizeof(f)); 29 for(int i = 1;i <= m;i ++)f[0][i] = 0; 30 for(int i = 1;i <= n;i ++) 31 { 32 for(int j = x[i] + 1;j <= m + x[i];j ++)//上升是完全背包 33 { 34 f[i][j] = min(f[i - 1][j - x[i]] + 1,f[i][j - x[i]] + 1); 35 } 36 for(int j = m + 1;j <= m + x[i];j ++) 37 { 38 f[i][m] = min(f[i][m],f[i][j]); 39 } 40 for(int j = 1;j <= m - y[i];j ++)//下降是01背包 41 { 42 f[i][j] = min(f[i][j],f[i - 1][j + y[i]]); 43 } 44 for(int j = 1;j <= low[i] - 1;j ++)f[i][j] = f[0][0]; 45 for(int j = high[i] + 1;j <= m;j ++)f[i][j] = f[0][0]; 46 } 47 int ans = f[0][0]; 48 for(int i = 1;i <= m;i ++) 49 { 50 ans = min(ans,f[n][i]); 51 } 52 if(ans < f[0][0])printf("1 %d ",ans); 53 else 54 { 55 int ans = 0; 56 for(int i = 1;i <= n;i ++) 57 { 58 bool suc = 0; 59 for(int j = 1;j <= m;j ++) 60 { 61 if(f[i][j] < f[0][0]) 62 { 63 suc = 1; 64 if(vis[i])ans++; 65 break; 66 } 67 } 68 if(!suc)break; 69 } 70 printf("0 %d ",ans); 71 } 72 return 0; 73 }