首页 > 代码库 > dp的练习题;

dp的练习题;

老刘在网上放了dp的比赛,全?是dp;

菜如狗的lsj发现自己什么都不会,于是开了这篇随笔来巩固(重学)一下dp;

B - 最长等差数列

N个不同的正整数,找出由这些数组成的最长的等差数列。

 
 
例如:1 3 5 6 8 9 10 12 13 14
等差子数列包括(仅包括两项的不列举)
1 3 5
1 5 9 13
3 6 9 12
3 8 13
5 9 13
6 8 10 12 14
 
其中6 8 10 12 14最长,长度为5。
 
 

Input第1行:N,N为正整数的数量(3 <= N <= 10000)。 
第2 - N+1行:N个正整数。(2<= Aii <= 10^9)Output最长等差数列的长度。Sample Input

10
1
3
5
6
8
9
10
12
13
14

Sample Output

5
先用sort拍一下序;dp【i】【u】表示结尾是a[i],倒数第二个是a[u]的等差数列的长度;那么我们就可以列出方程:
if(a[i]-a[u]==a[u]-a[k])dp[i][u]=dp[u][k]+1;
看似好像是n^3, 但仔细分析一下的话会发现由于u是递增的,所以a[i]-a[u]是递减的;所以a[u]-a[k]也是递减的,所以k是递增的;
有了这个想法,就不难写出n^2的算法了;
技术分享
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define xx getchar()
 7 #define tmp 23
 8 using namespace std;
 9 int a[10001];
10 short dp[10001][10001];
11 int n;
12 short ans=0;
13 int read()
14 {
15     int x=0,f=1;
16     char ch;
17     ch=xx;
18     while(ch<0||ch>9){if(ch==-)f=-1;ch=xx;}
19     while(ch>=0&&ch<=9){x=(x<<1)+(x<<3)+ch-0;ch=xx;}
20     return x*f;
21 }
22 int main()
23 {
24     n=read();
25     for(int i=1;i<=n;i++)
26         a[i]=read(),dp[i][0]=1;
27     sort(a+1,a+n+1);
28     for(int i=2;i<=n;i++)
29     {
30         int k=0;
31         for(int u=1;u<i;u++)
32         {
33             int x=a[u]-(a[i]-a[u]);
34             int f=0;
35             while(k<=u)
36             {
37                 if(a[k]==x){dp[i][u]=dp[u][k]+1;f=1;}
38                 if(a[k]>x)break;
39                 k++;
40             }
41             if(f==0)dp[i][u]=2;
42             ans=max(ans,dp[i][u]);
43         }
44     }
45     cout<<ans<<endl;
46     return 0;
47 }
View Code

C - 最大M子段和

 

N个整数组成的序列a1,a2,a3,…,an,将这N个数划分为 互不相交的M个子段,并且这M个子段的和是最大的。 如果M >= N个数中正数的个数,那么输出所有正数的和。
例如: -2  11  -4  13  -5  6  -2,分为2段,11 -4 13一段,6 一段,和为26。

Input第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 5000)
第2 - N+1行:N个整数 (-10^9 <= aii <= 10^9)Output输出这个最大和Sample Input

7 2
-2
11
-4
13
-5
6
-2

Sample Output

26

设dp【i】【u】将前u个数分成i段的最大值;
刚开始想的是这样的;
do[i][u]=max(dp[i-1][u],dp[i][u-1]);
dp[i][u]=max(dp[i-1][u-1]+a[u],dp[i][u-1]+a[u],dp[i][u]);
但是这样是有问题的;
dp[i-1][u-1]+a[u]并不错但dp【i】【u-1】+a[u]就不一定了;
因为第i个不一定连续到了u-1;
那么我们就想一下如何保证dp【i】【u-1】+a【u】是可行的;
经过深思熟虑的思考(查题解);
我们知道了这样一个方式:保证a[u-1]在i中;
再开一个f[i][u]记录a[u]必选的最大值;
那么就有f[i][u]=max(f[i-1][u-1]+a[u],f[i][u-1]+a[u]);
那么dp[i][u]就可以改成dp[i][u]=max(dp[i-1][u-1],f[i][u]);
这样就可以A掉此题;
还值得说的就是对此题的空间优化了,因为f,dp数组都只和上一个f,dp有关,所以我们可以吧i给省略掉;
技术分享
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define xx getchar()
 7 #define tmp 23
 8 #define intt long long
 9 using namespace std;
10 int m;
11 int n;
12 short ans=0;
13 int read()
14 {
15     int x=0,f=1;
16     char ch;
17     ch=xx;
18     while(ch<0||ch>9){if(ch==-)f=-1;ch=xx;}
19     while(ch>=0&&ch<=9){x=(x<<1)+(x<<3)+ch-0;ch=xx;}
20     return x*f;
21 }
22 intt a[10020];
23 intt dp[3][5001];
24 intt f[3][5001];
25 int b[10000];
26 intt sum[10000];
27 intt ansx=0;
28 int numz=0;
29 int main()
30 {
31     int k=0;
32     n=read();m=read();
33     for(int i=1;i<=n;i++)
34     {a[i]=read();b[i]=k;if(a[i]>0)numz++,ansx+=a[i],k=i;sum[i]=sum[i-1]+a[i];}
35     if(m>=numz){cout<<ansx<<endl;return 0;}
36     for(int i=1;i<=m;i++)
37     {
38         for(int u=1;u<=n;u++)
39         {
40             f[2][u]=max(dp[1][u-1]+a[u],f[2][u-1]+a[u]);
41             //if(a[u]>0)
42             //dp[2][u]=max(dp[1][u-1]+a[u],dp[2][b[u]]+sum[u]-sum[b[u]]);
43             dp[2][u]=max(f[2][u],dp[2][u-1]);
44         }
45         for(int u=1;u<=n;u++)
46         dp[1][u]=dp[2][u],f[1][u]=f[2][u];
47     }
48     cout<<dp[1][n]<<endl;
49     return 0;
50 }
View Code

D - 树的距离之和

给定一棵无根树,假设它有n个节点,节点编号从1到n, 求任意两点之间的距离(最短路径)之和。Input第一行包含一个正整数n (n <= 100000),表示节点个数。 
后面(n - 1)行,每行两个整数表示树的边。Output每行一个整数,第i(i = 1,2,...n)行表示所有节点到第i个点的距离之和。Sample Input

4
1 2
3 2
4 2

Sample Output

5
3
5
5

我们分析一下,对任意点的距离其实=e【i】.v*经过e【i】的次数;所以我们可以以边为单位来解决这个问题;

那么当根节点顺着边转移的时候,记次边原本被经过了x次,那么现在就变成了n-x次;而别的边是不变的;

所以我们只需要先去一个点来建一颗书,然后再递推出来答案就好了;

记得用longlong;

技术分享
 1 #include<iostream>
 2 #include<cstring>
 3 #define intt long long 
 4 using namespace std;
 5 int read()
 6 {
 7     int x=0,f=1;
 8     char ch;
 9     ch=getchar();
10     while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
11     while(ch>=0&&ch<=9){x=(x<<1)+(x<<3)+ch-0;ch=getchar();}
12     return x*f;
13 }
14 struct {int v,next,y,xxx;}e[1000000];
15 int lin[1000000];
16 int len=0;
17 intt n;
18 intt ans=0;
19 intt ansx[1000000]={};
20 int init(int x,int y,int v)
21 {e[++len].y=y;e[len].v=v;e[len].next=lin[x];lin[x]=len;e[len].xxx=0;}
22 struct sss{intt vis,v;};
23 int    dfs(int x,int f,int vv)
24 {    
25     int viss=0;
26     for(int i=lin[x];i;i=e[i].next)
27     {
28         int y=e[i].y;
29         if(y==f)continue;
30         ans+=vv+e[i].v;
31         e[i].xxx=dfs(y,x,vv+e[i].v);
32         viss+=e[i].xxx;
33     }
34     return viss+1;
35 }
36 void dfsx(int x,int f)
37 {
38     for(int i=lin[x];i;i=e[i].next)
39     {
40         int y=e[i].y;
41         if(y==f)continue;
42         int w=n-e[i].xxx;
43         ansx[y]=ansx[x]+(w-e[i].xxx)*e[i].v;
44         dfsx(y,x);
45     }
46     return ;
47 }
48 void initx()
49 {
50     n=read();
51 //cout<<n<<endl;
52     for(int i=1;i<=n-1;i++)
53     {
54         int x=read();
55         int y=read();
56         init(x,y,1);init(y,x,1);
57     }
58     dfs(1,0,0);
59     ansx[1]=ans;
60     dfsx(1,0);
61 }
62 int main()
63 {
64     initx();
65     for(int i=1;i<=n;i++)
66     {
67         cout<<ansx[i]<<endl;
68     }
69     return 0;
70 }
View Code

 

 

dp的练习题;