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 }

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 }

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 }

时间: 2024-10-13 15:21:53

dp的练习题;的相关文章

HDU ACM 4734 F(x)-&gt;数位DP入门练习题

分析:数位DP的入门联系题,通过该題对数位DP有感觉了:dp[len][presum]代表长度为len权值不大于presum的数.记忆化搜索提高效率,除边界(因为边界上算出来的答案在其它数中可能是不完整的)外记录dp[len][presum]的值,下次发现以前计算过就直接return:dfs(len, presum, fg)表示求长度为len,不超过pre的全部符合条件的值.fg是控制边界的. #include<iostream> using namespace std; int dp[11]

线性动态规划

准确来说,动态规划是一种思想,而不是一种算法.算导里将它归结为——高级程序设计技巧. 在线性结构上进行状态转移DP,统称线性DP. 线性DP最常见的有: 子集和问题,LIS问题,LCS问题. 拓展之后有:子段和问题,杂类问题. 1. 子集和问题和硬币计数问题 子集和问题的一个实例: 〈S,t〉.其中,S={ 1 x , 2 x ,…, n x }是一个正整数的集合,c是一个正整数.子集和问题判定是否存在S的一个子集S1,使得s1中的各元素之和等于c. 这其实就是0/1背包. 硬币计数问题的一个实

台阶问题练习题 (简单的dp)

题目: 有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法.为了防止溢出,请将结果Mod 1000000007 给定一个正整数int n,请返回一个数,代表上楼的方式数.保证n小于等于100000. 测试样例:1 返回: 1 到达n台阶时,可以有两种方法到达n台阶,只需走一级就可以上去,或者只需走两级就可以上去,哦了 昨天晚上搞得dp几道经典题目,抽个时间把那几道在理解下. 1 #include <cstdio> 2 #include <algorithm> 3 u

XTU 二分图和网络流 练习题 C. 方格取数(1)

C. 方格取数(1) Time Limit: 5000ms Memory Limit: 32768KB 64-bit integer IO format: %I64d      Java class name: Main 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20) Output 对

dp新手入门

网址转载链接:  http://bbs.chinaunix.net/thread-4094539-1-1.html 动态规划:从新手到专家 Hawstein翻译 前言 我们遇到的问题中,有很大一部分可以用动态规划(简称DP)来解. 解决这类问题可以很大地提升你的能力与技巧,我会试着帮助你理解如何使用DP来解题. 这篇文章是基于实例展开来讲的,因为干巴巴的理论实在不好理解. 注意:如果你对于其中某一节已经了解并且不想阅读它,没关系,直接跳过它即可. 简介(入门) 什么是动态规划,我们要如何描述它?

从一道简单的dp题中学到的...

今天想学点动态规划的知识,于是就看了杭电的课件,数塔问题啊,LCS啊都是比较经典的动规了,然后随便看了看就开始做课后练习题... HDOJ 1421 搬寝室 http://acm.hdu.edu.cn/showproblem.php?pid=1421 题目大意:从n(n <= 2000)个数中选出k对数(即2*k个),使它们的差的平方和最小. 例如:从8,1,10,9,9中选出2对数,要使差的平方和最小,则应该选8和9.9和10,这样最小,结果为2 因为知道是dp的题,先建个dp[][]数组,然

DP(动态规划)从新手到专家 ——转自Hawstein

这是我第一篇博文,本想自己写一篇,但是看他的DP讲的太好了,就转载了一下: ----------------------------------------------------------分界线 作者:Hawstein出处:http://hawstein.com/posts/dp-novice-to-advanced.html声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处. 前言 本文翻

UVA - 11825 状压DP

该题目是EMAXX推荐的练习题,刘汝佳的书也有解说 如果S0属于全集,那S0就可以作为一个分组,那么S分组数可以是best{当前S中S0的补集+1} 对于集合类的题目我觉得有点抽象,希望多做多理解把 #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<

01背包练习题

01背包练习题 题目 一个背包有一定的承重cap,有N件物品,每件都有自己的价值,记录在数组v中,也都有自己的重量,记录在数组w中,每件物品只能选择要装入背包还是不装入背包,要求在不超过背包承重的前提下,选出物品的总价值最大. 给定物品的重量w价值v及物品数n和承重cap.请返回最大总价值. 测试样例: [1,2,3],[1,2,3],3,6 返回:6 解析 class Backpack { public: int maxValue(vector<int> w, vector<int&g