一些DP基础题(1)

HDU 1024  Max Sum Plus Plus

Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem. 
Given a consecutive number sequence S 1, S 2, S 3, S 4 ... S x, ... S n (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ S x ≤ 32767). We define a function sum(i, j) = S i + ... + S j (1 ≤ i ≤ j ≤ n).  
Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i 1, j 1) + sum(i 2, j 2) + sum(i 3, j 3) + ... + sum(i m, j m) maximal (i x ≤ i y ≤ j x or i x ≤ j y ≤ j x is not allowed).  
But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(i x, j x)(1 ≤ x ≤ m) instead. ^_^
Input

Each test case will begin with two integers m and n, followed by n integers S 1, S 2, S 3 ... S n.
Process to the end of file.
Output

Output the maximal summation described above in one line.


Sample Input

1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Sample Output
6
8

Hint

Huge input, scanf and dynamic programming is recommended.

题意:从给出的n个ai中,选出m组连续不重叠的子序列,使得和最大,求最大和。

/*dp[j]:前j个数中最大的序列的和
mark[j]记录迄今为止最大的序列和*/
(虽说是基础题,但是真的想不到啊……巨难理解有木有!窝看了好久好久啊……(⊙﹏⊙)难道是我智商太低)
emmmmmmmm……还是先从简化为滚动数组之前的一般递推式说起……
dp[i][j]:前j个元素取i段的最大子段和
根据大佬们的题解,我的理解是,第i段序列每次取aj的时候有2种情况:
①aj为第i段序列的段首(第i-1段已达到最大,加上aj后第i-1段减小了)
②aj不是第i段序列的段首(第i段加上aj以后会更大)
那么,如果dp[i-1][k](i<k<j)(前j-1个元素取i-1段的最大子段和,也就是aj作为第i段段首)和dp[i][j-1](前j-1个元素取i段的最大子段和,也就是aj作为第i-1段的非段首)均已知,
它们中的最大值加上aj就是我们要的答案,由此可以得到递推关系:
dp[i][j]=max(dp[i][j-1],max(dp[i-1][k]))+a[j]; (i<k<j)

上边的想通了,这个题马上就能A了( •̀ ω •́ )y

由于n的大小是1,000,000,虽然窝对数字好像也不太敏感┭┮﹏┭┮,但是想想也是有点大,而且又是O(n^3)想想都会超
所以这里用滚动数组来处理,观察上面的递推关系,
dp[i][j]=max(dp[i][j-1],max(dp[i-1][k]))+a[j]; 
加下划线的两部分之间i是共同的,只有k和j的区别,可以用两个一维数组分别记录,至于i和i-1只要处理一下更新的时间就可以了
终于,一开始提到的两个一维数组要出场了,如何处理,看代码就好了`>w<`
dp[j]:前j个数中最大的序列的和(前i-段+当前第i段)
mark[j]:迄今为止的最大的序列和(前i-1段)


 1 #include<iostream>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 int dp[1000005];
 6 int mark[1000005];
 7 int a[1000005];
 8 int m, n,ans;
 9 int main()
10 {
11     while (cin >> m >> n)
12     {
13         memset(dp, 0, sizeof(dp));
14         memset(mark, 0, sizeof(mark));
15         for (int i = 1; i <= n; i++)
16             cin >> a[i];
17         for (int i = 1; i <= m; i++) //前j个数字取i段
18         {
19              ans = -0x3f3f3f3f;   //注意初始化,之后的子列和可能比当前的小,所以用-inf
20             for (int j = i; j <= n; j++) //dp[j]记录前j-1个数字的最大子段和 和 当前子段和
21             {
22                 dp[j] = max(dp[j - 1], mark[j - 1]) + a[j]; 
23 mark[j - 1] = ans; //这里更新mark,第i段用到的mark其实是第i-1段的
24 ans = max(dp[j], ans); 25 } 26 } 27 cout << ans << endl; 28 } 29 return 0; 30 }

 


 

HDU 1087  Super Jumping! Jumping! Jumping!
Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. Maybe you are a good boy, and know little about this game, so I introduce it to you now. 



The game can be played by two or more than two players. It consists of a chessboard(棋盘)and some chessmen(棋子), and all chessmen are marked by a positive integer or “start” or “end”.
The player starts from start-point and must jumps into end-point finally. In the course of jumping, the player will visit the chessmen in the path, but everyone must jumps from one chessman
to another absolutely bigger (you can assume start-point is a minimum and end-point is a maximum.). And all players cannot go backwards. One jumping can go from a chessman to next, also can
go across many chessmen, and even you can straightly get to end-point from start-point. Of course you get zero point in this situation. A player is a winner if and only if he can get a bigger score
according to his jumping solution. Note that your score comes from the sum of value on the chessmen in you jumping path.
Your task is to output the maximum value according to the given chessmen list.
InputInput contains multiple test cases. Each test case is described in a line as follow:
N value_1 value_2 …value_N
It is guarantied that N is not more than 1000 and all value_i are in the range of 32-int.
A test case starting with 0 terminates the input and this test case is not to be processed.
OutputFor each case, print the maximum according to rules, and one line one case.
Sample Input
3 1 3 2
4 1 2 3 4
4 3 3 2 1
0
Sample Output
4
10
3


题意:每次只能跳到比之前更大的点,总之,求最大上升子列和
这个递归要比前一个题好想多了,可以设二维数组dp[j]录前j个元素的最大上升子列和,在考虑当前ai时,满足j<i&&aj<ai,就加上ai,最后暴力搜索一边dp,最大值即为所求
 1 #include<iostream>    
 2 #include<algorithm>
 3 #include<string>
 4 using namespace std;
 5 int n, a[1005];
 6 int dp[10005];
 7 int main()
 8 {
 9     while (cin >> n&&n)
10     {
11         for (int i = 0; i < n; i++)
12             cin >> a[i];
13         dp[0] = a[0];
14         for (int i = 1; i < n; i++)
15         {
16             dp[i] = a[i];
17             for (int j = 0; j < i; j++)
18             {
19                 if (a[i] > a[j])
20                     dp[i] =max(dp[i], dp[j] + a[i]);
21             }
22         }
23         int maxx = 0;
24         for (int i = 0; i < n; i++)
25             if (dp[i] > maxx)
26                 maxx = dp[i];
27         cout << maxx << endl;
28     }
29     return 0;
30 }


HDU 1257   最少拦截系统

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input

8 389 207 155 300 299 170 158 65

Sample Output

2

题意:中文题……不解释……
跟上一个题求最长上升子列没什么太大的区别,只不过这里改成求得东西不太一样,DP数组存储的是最长上升子列的长度……
只需要每次出现上升子列时更新一下DP数组即可
emmmmmm直接附代码吧

#include<iostream>  
#include<algorithm>
#include<string>
using namespace std;
int a[10000];
int t,n,crt;
int dp[10000];
int main()
{
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
        {
            cin >> a[i];
        }
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i < n; i++)
            for (int j = 0; j < i; j++)
                if (a[i] > a[j])
                    if (dp[i] < dp[j] + 1)
                        dp[i] = dp[j] + 1;
        int maxx = 0;
        for (int i = 0; i < n; i++)
            if (maxx < dp[i])
                maxx = dp[i];
        cout << maxx+1 << endl;
    }
    return 0;
}
这里再甩一个用lower_bound 的代码,求最长上升子列长度的,很好用
 1 #include<iostream>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 int n, a[10000],dp[10000];
 6 int main()
 7 {
 8     while (cin >> n)
 9     {
10         memset(dp, 0x3f3f3f3f, sizeof(dp));
11         for (int i = 0; i < n; i++)
12             cin >> a[i];
13         for (int i = 0; i < n; i++)
14             *lower_bound(dp, dp + n,a[i]) = a[i];
15         cout << lower_bound(dp, dp + n, 0x3f3f3f3f) - dp<<endl;
16     }
17     return 0;
18 }

 

 

 Ignatius and the Princess IV

"OK, you are not too bad, em... But you can never pass the next test." feng5166 says.

"I will tell you an odd number N, and then N integers. There will be a special integer among them, you have to tell me which integer is the special one after I tell you all the integers." feng5166 says.

"But what is the characteristic of the special integer?" Ignatius asks.

"The integer will appear at least (N+1)/2 times. If you can't find the right integer, I will kill the Princess, and you will be my dinner, too. Hahahaha....." feng5166 says.

Can you find the special integer for Ignatius?
InputThe input contains several test cases. Each test case contains two lines. The first line consists of an odd integer N(1<=N<=999999) which indicate the number of the integers feng5166 will tell our hero. The second line contains the N integers. The input is terminated by the end of file.
OutputFor each test case, you have to output only one line which contains the special number you have found.
Sample Input

5
1 3 2 3 3
11
1 1 1 1 1 5 5 5 5 5 5
7
1 1 1 1 1 1 1

Sample Output

3
5
1

题意:特别的数就是!!!出现次数超过(n+1)/2的数……
emmmmmmmm ……直接暴力搜出现最多的数也可以……然而……记住,这是DP…这是DP……
这里甩两个代码,一个是懒懒的用了map的,另一个用DP的思想……(突然发现我怎么这么喜欢用map啊……)
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
map<int,int> a;
int n,m;
int main()
{
    while (cin >> n)
    {
        a.clear();
        for(int i=0;i<n;i++)
        {
            cin >> m;
            a[m]++;
        }
        map<int, int>::iterator it;
        for (it = a.begin(); it != a.end();it++)
            if (it->second >= (n + 1) / 2)
            {
                cout << it->first << endl;
                break;
            }
    }
    return 0;
}

如果一个数出现了(n+1)/2以上的次数的话,那么它的总数减去出现过的其他的数剩下的个数>0
#include<iostream>
using namespace std;
map<int,int> a;
int n,m,x,y;
int main()
{
    while (cin >> n)
    {
        cin >> m;         //y用来记录↓出现的次数
        x = m; y = 1;   //x用来记录我们要找的出现次数超过(n+1)/2次的数
        for (int i = 1; i < n; i++)
        {
            cin >> m;
            if (m == x)
                y++;    //出现一次+1;
            else if (y > 0)
                y--;      //否则-1;
            else               //如果至今为止的总数减去其他的数的个数<=0
            {                   //说明这不是我们要找的数,更新x记录的值
                x = m;
                y = 1;
            }
        }
        cout << x << endl;
    }
    return 0;
}

 

 

 

 

posted @ 2017-08-18 21:59  #Egoist#  阅读(322)  评论(0编辑  收藏  举报