20191110noip2014飞扬的小鸟~ | 背包dp(完全背包)(01背包)

luogu1941飞扬的小鸟

不得不说出这题的人太强了!!!此题结合了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\n%d\n",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\n%d\n",ans);
71     }
72     return 0;
73 }

猜你喜欢

转载自www.cnblogs.com/djfuuxjz/p/11832168.html