dp杂题(根据个人进度选更)

----19.7.30 今天又开了一个新专题,dp杂题,我依旧按照之前一样,这一个专题更在一起,根据个人进度选更题目;

dp就是动态规划,本人认为,动态规划的核心就是dp状态的设立以及dp转移方程的推导,这也是训练的重中之重,所以代码不那么重要,重要的就是dp的思想;

T1:

A. 消失之物

题目描述

ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢?” -- 这是经典的问题了。她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。

这道题乍一看以为就是一个裸的背包,但是(本人太弱了)我以上来先想到的是裸的$ 0/1 $背包然后就想跑$ n $遍$  0/1 $背包,但是显然复杂度会爆炸,所以要考虑别的方法,但是还是逃不掉的dp,重点就在状态的转移,这个转移其实可以在之前的0/1背包的基础上进行转移,那么我们可以设f[maxn][2],也就是开一维半的数组,设状态为$ f[j][1] $表示背包容量为j时的方案数,因为题目恶心了我们就是要输出一个矩阵,那么我们就需要再循环i表示当我们去掉i时$  f[j][1] $就是背包容量为j时的方案数,那么就可以列出状态转移方程:(这里的0/1表示能否可以解决!)

$  f[j][0]+=f[j-v[i]] $

$  f[j][1]=f[j][0]+f[j-v[i]][1] (j-v[i]>0)$

$  f[j][1]=f[j][0]  (j-v[i]<=0) $

然后就结束了,一定要记得多多取模(他让输出最低的一位,所以不那么恶心!),这道题没什么细节,就不站代码了,评论区留给你们!

 UPD:这道题skyh的打法刷新了我的dp观,我是真的震惊

天黄的这道题使用分治带dp打的,很新颖,不愧是dalao(orz),那么我也稍说一下skyh的思路:

$ (after 10 mins...) $其实和我的思路差不多,就是分治了一下,qwq

 

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N=2010;
 5 int n,m,w[N];
 6 short dp[15][N];
 7 void solve(int dep,int l,int r){
 8     if(l==r){
 9         for(int i=1;i<=m;++i) printf("%d",dp[dep-1][i]);
10         puts("");
11         return ;
12     }
13     int mid=l+r>>1;
14     for(int i=0;i<=m;++i) dp[dep][i]=dp[dep-1][i];
15     for(int i=mid+1;i<=r;++i) for(int j=m;j>=w[i];--j) (dp[dep][j]+=dp[dep][j-w[i]])%=10;
16     solve(dep+1,l,mid);
17     for(int i=0;i<=m;++i) dp[dep][i]=dp[dep-1][i];
18     for(int i=l;i<=mid;++i) for(int j=m;j>=w[i];--j) (dp[dep][j]+=dp[dep][j-w[i]])%=10;
19     solve(dep+1,mid+1,r);
20 }
21 int main()
22 {
23     scanf("%d%d",&n,&m);
24     for(int i=1;i<=n;++i) scanf("%d",&w[i]);
25     dp[0][0]=1; solve(1,1,n);
26     return 0;
27 }
skyh

//copyright by skyh

//copy from skyh  orz

 

B. 方伯伯的玉米田

内存限制:128 MiB 时间限制:6000 ms 标准输入输出
 
 
题目描述

方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。
这排玉米一共有N株,它们的高度参差不齐。
方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。
方伯伯可以选择一个区间,把这个区间的玉米全部拔高1单位高度,他可以进行最多K次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。
问能最多剩多少株玉米,来构成一排美丽的玉米。

输入格式

第1行包含2个整数n,K,分别表示这排玉米的数目以及最多可进行多少次操作。
第2行包含n个整数,第i个数表示这排玉米,从左到右第i株玉米的高度ai。

输出格式

输出1个整数,最多剩下的玉米数。

这是学长讲过的一道例题,是数据结构优化dp,而且这道题需要证明一个引理;

引理:所有操作的右端点一定是n(最右侧)的那个点。

证明:如果将一个区间内的权值都加上一个数,只会出现两种情况:

  1.区间的左侧:

    之前比区间内的数小的在操作之后还是比他小;

    之前比区间内的数大的在操作之后没有他大(比他小!);

    之前比区间内的数大的在操作之后还是比他大;

  所以区间左侧不会降低ans,还可能增加ans

  2.区间的右侧:

    之前比区间内的数小在操作之后还是比他小;

    之前比区间内数大的,现在不一定比他大;

    之前比区间内数大的,现在还是比他大;

 所以区间右侧不会升高ans,还可能降低ans;

所以要保证答案最优,就要有区间右侧最小,所以就有所有的操作都以n为有区间的端点;

证明完毕;

接着回到题解,这里有了上面的引理,我们就能推出dp的状态转移方程;

设$ f[i][j] $表示以i为结尾,共被拔高了j次的ans,即以i为结尾,共被j个区间覆盖;

那么,我们根据定义可以推出状态转移方程:

  $ f[i][j]=max{f[k][l]}+1 (1<=k<=i,1<=l<=j ) $且要合法才能转移;

那么这一看如果暴力求解的话复杂度爆表,所以这个可以使用二维树状数组进行优化,然后就是$ O(n*m) $的复杂度;

代码实现也很简单:

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstdio>
 6 using namespace std;
 7 #define re register
 8 int a[10005],c[10005][505],n,m,maxa,ans,sum,res;
 9 int lowbit(int x){return x&(-x);}
10 void change(int x,int y,int z)
11 {
12     int yy=y;
13     while(x<=maxa+m)
14     {
15         y=yy;
16         while(y<=m+1)
17         {
18             c[x][y]=max(c[x][y],z);
19             y+=lowbit(y);
20         }
21         x+=lowbit(x);
22     }
23 }
24 int getsum(int x,int y)
25 {
26     int yy=y,sum=0;
27     while(x>0)
28     {
29         y=yy;
30         while(y>0)
31         {
32             sum=max(sum,c[x][y]);
33             y-=lowbit(y);
34         }
35         x-=lowbit(x);
36     }
37     return sum;
38 }
39 int main()
40 {
41     //freopen("simple.txt","r",stdin);
42     scanf("%d%d",&n,&m);
43     for(int i=1;i<=n;i++)
44     {
45         scanf("%d",&a[i]);
46         maxa=max(maxa,a[i]);
47     }
48     for(int i=1;i<=n;i++)
49     {
50         for(int j=m;j>=0;j--)
51         {
52             res=getsum(a[i]+j,j+1)+1;
53             change(a[i]+j,j+1,res);
54             ans=max(ans,res);
55         }
56     }
57     printf("%d\n",ans);
58     return 0;
59 }
玉米田

 

 

 

///////这个专题的坑还很大,未完待续.........////////

转载于:https://www.cnblogs.com/hzoi-lsc/p/11272454.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值