Basic DP Introduction [ HDU 2084 ] Number Tower (Digital Triangle) and [ HDU 1176 ] Free Pies and [ HDU 1257 ] Least Intercept System and [ HDU 1421 ] Moving Dorms

Getting Started with Basic DP

  
 

HDU 2084 Data Tower

Link: http://acm.hdu.edu.cn/showproblem.php?pid=2084
or AcWing (y always speaks very well): https://www.acwing.com/problem/content/900/

Problem Description

When talking about the DP algorithm, a classic example is the tower problem, which is described as follows:

There is a number tower as shown below. It is required to go from the top floor to the bottom floor. If each step can only reach adjacent nodes, what is the maximum sum of the numbers of the nodes passed?
insert image description here
I have already told you that this is a DP question, can you do AC?

Input

The input data first includes an integer C, indicating the number of test instances, and the first line of each test instance is an integer N (1 <= N <= 100), indicating the height of the data tower, followed by N lines of numbers A number tower, where there are i integers in the i-th row, and all integers are in the interval [0,99].

Output

For each test instance, output the maximum possible sum, one line per instance.

Sample Input

1
5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5

Sample Output

30

  
 

train of thought

    This question directly enumerates each path and then compares the size, which is obviously not enough. It is easy to think of dynamic programming (although the data is very weak, nothing can be seen).
It can be found that finding the number and maximum of each point can be transformed into finding the same sub-problem plus itself. And each sub-problem is optimal, and the final solution is optimal.

According to experience, it is natural to set the state expression as f [ i ] [ j ] f[i][j]f [ i ] [ j ] respectively represent the maximum value of the digital sum to each point.
For the problem of counting towers, we generally have two ways of thinking about traversal solutions: top-down and bottom-up.

top down

In this traversal solution, each state expresses f [ i ] [ j ] f[i][j]f [ i ] [ j ] represents a set, which means from the top vertex to( i , j ) (i, j)(i,j ) All paths to this location. And this set can be further divided into two parts, one part reaches this point from the upper left, and the other part reaches this point from the upper right, (there is only one part of the boundary condition) and then calculate the two part numbers in this set and the big one, that is, Down state calculations to be analyzed.

insert image description here

The picture comes from ( https://blog.csdn.net/yanweiqi1754989931/article/details/113096389#comments_14882773 ) Thanks!

insert image description here
When passing through a certain node A, the maximum sum of the numbers on the path to A = the upper left and upper right of A, the two nodes B and C that can reach the largest sum of numbers + the number of node A, we will It is written as the state transition equation:

f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] ) + n u m [ i ] [ j ] f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] ) + n u m [ i ] [ j ] f[i][j]=max(f[i1][j],f[i1][j1])+n u m [ i ] [ j ]

Among them, i , ji , ji,j represent the coordinates of the row and column respectively,ffThe f array is an array that maintains the maximum sum, and the num array stores the input numbers

In this example, we choose 1 in the third line for analysis:
we first seek the maximum sum that can be achieved by the number 3 in the upper left corner and the number 8 in the upper right corner:
3 obviously has only one path of 7-3, so the maximum sum is 10 ; 8 obviously has only one path of 7-8, and its maximum
sum is 15;
After cyclically calculating each node, scan the last line, and the maximum value obtained is the result

In this way we know the method of state calculation.

In the same way, it can be seen that the bottom-up method can reduce some boundary judgments when solving bottom-up. Of course, you can also expand the point array from top to bottom. It does not matter if the place beyond the boundary is set to 0. No need to Judgment, if it is convenient to write code here, consider writing code in a bottom-up method.

bottom up

The difference from the top-down type is that when the state is transferred, the max is taken from the lower left corner and the lower right corner of the number (for the position of the original number tower).

insert image description here

The picture comes from ( https://blog.csdn.net/yanweiqi1754989931/article/details/113096389#comments_14882773 ) Thanks!

In this way, there will be no difference in boundary conditions, and each point is solved through the two points below. And the last step of finding the max of the last row is missing, and the highest point is stored as the maximum value.

State transition equation:

f [ i ] [ j ] = m a x ( f [ i + 1 ] [ j ] , f [ i + 1 ] [ j + 1 ] ) + n u m [ i ] [ j ] f[ i ][ j ] = max ( f[ i + 1 ][ j ] , f[ i + 1 ][ j + 1 ] ) + num [i][j] f[i][j]=max(f[i+1][j],f[i+1][j+1])+n u m [ i ] [ j ]

 

Code

#include<iostream>
#include<algorithm>

using namespace std;

const int  N = 510;
int n;
int w[N][N],f[N][N];

int main()
{
    
    
    int c;
    cin >> c;
    while(c --)
    {
    
    
        cin >> n;
        for(int i = 1; i <= n; i ++)
            for(int j = 1 ;j <= i; j ++)
                cin >> w[i][j];
        for (int i = 1; i <= n; i ++) f[n][i]=w[n][i];
        for (int i = n - 1; i; i --)
        	for (int j = 1; j <= i; j ++)
        		f[i][j]=w[i][j]+max(f[i+1][j],f[i+1][j+1]);
        cout << f[1][1] << endl;
    }
    
    return 0;
}

  
 
 

HDU 1176 Free Pies

Link: http://acm.hdu.edu.cn/showproblem.php?pid=1176

Problem Description

It is said that there will be no pies in the sky, but one day Gameboy was walking on the path home when suddenly a lot of pies fell from the sky. Speaking of Gameboy's character is really good, this pie doesn't drop anywhere else, it just falls within 10 meters of him. If the pie falls on the ground, of course it cannot be eaten, so gameboy immediately unloads his backpack to pick it up. But since no one can stand on both sides of the path, he can only pick up on the path. Because Gameboy usually stays in the room to play games, although he is a master of agility in the game, his motor nerves are extremely slow in reality, and he can only catch falling pies within a range of no more than one meter per second. Now put coordinates on this path such as the icon:
insert image description here
In order to simplify the problem, suppose that in the next period of time, the pies will fall in the 11 positions of 0-10. At the beginning, gameboy stands at position 5, so in the first second, he can only receive pies in one of the three positions 4, 5, and 6. Ask how many pies gameboy can receive at most? (assuming his knapsack can hold an infinite number of pies)

Input

The input data has multiple groups. The first line of each set of data is a positive integer n (0<n<100000), indicating that there are n pies falling on this trail. In the next n lines, each line has two integers x, T (0<T<100000), indicating that a pie falls on point x at the Tth second. Multiple pies may drop at the same point in the same second. The input ends when n=0.

Output

Each set of input data corresponds to a row of output. Output an integer m, indicating that the gameboy may receive up to m pies.
Tip: The amount of input data in this question is relatively large, it is recommended to use scanf to read in, and cin may time out.

Sample Input

6
5 1
4 1
6 1
7 2
7 2
8 3
0

Sample Output

4

 
 

train of thought

insert image description here

The picture comes from ( https://blog.csdn.net/yanweiqi1754989931/article/details/113096389#comments_14882773 ) Thanks!

The connection method of the example is: move to x = 6 on the 1st s to catch a pie at x = 6, move to x = 7 on the 2nd s, catch two pies on x = 7, and move to x = 7 on the 3rd s 8. Catch a pie at x = 8, for a total of 4 pies.
After reading the sample, let's continue to analyze the topic: the coordinate points are fixed, and the number of pies that can be received by each point can also be counted. After drawing this picture and adding the time axis, is it a bit like the above The numbers are triangles, so we can convert the problem into a number tower model. It's just that the last question is a maximum of two choices, but here are three choices, and the boundary conditions that need more attention here.

insert image description here
Like the coordinate axis in the figure, we regard the time dimension as the "row" in the data tower model, and the columns do not need to be escaped to form the data tower. The sum is also the largest.
The sample simulation is
to calculate the row of 2s for the first time and get the result in the figure below.
insert image description here
Calculate the row of 1s for the second time and get the result in the figure below
insert image description here
and finally get the result
insert image description here

Thanks to the superiority of the bottom-up analysis final conclusion: the highest row in the end point column is the answer, so we adopt a bottom-up solution. And the next thing here is to push forward from the last second, and it is easy to know the state transition equation:
dp [ i ] [ j ] = dp [ i ] [ j ] + max ( max ( dp [ i + 1 ] [ j ] , dp [ i + 1 ] [ j + 1 ] ) , dp [ i + 1 ] [ j − 1 ] ) dp [ i ] [ j ] = dp [ i ] [ j ] + max ( max ( dp [ i + 1 ] [ j ] , dp [ i + 1 ] [ j + 1 ] ) , dp [ i + 1 ] [ j − 1 ] )dp[i][j]=dp[i][j]+max(max(dp[i+1][j],dp[i+1][j+1]),dp[i+1][j1])

 
 

Code

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

int dp[100009][15];

int main(){
    
    
    int n,x,t;
    while(scanf("%d", &n) != EOF && n){
    
    
        int m = 0;
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i ++){
    
    
            scanf("%d %d", &x, &t); 
            dp[t][x] ++; //每个时间点每个位置的馅饼数
            m = max(t, m);
        }
        for (int i = m - 1; i >= 0; i --){
    
     //从后往前算
            for (int j = 0; j <= 10; j ++){
    
    
            	int mx = 0;
            	if(j == 0)  mx = dp[i + 1][j + 1];
            	else if(j == 10)  mx = dp[i + 1][j - 1];
                else{
    
    
                	mx = max(dp[i + 1][j - 1], dp[i + 1][j + 1]);//两边位置下一秒的求得馅饼数的最大值
                }
                dp[i][j] += max(dp[i + 1][j], mx);
            }
        }
        printf("%d\n",dp[0][5]);
    }
    
    return 0;
}

 
 
 

HDU 1257 Minimum Intercept System

Link: http://acm.hdu.edu.cn/showproblem.php?pid=1257

Problem Description

A certain country has developed a missile interception system in order to defend against enemy missile attacks. However, this missile interception system has a flaw: although its first shell can reach any height, each subsequent shell cannot exceed the previous one. The height of the launch. One day, the radar caught the enemy's missile attack. Since the system is still in the trial stage, there is only one system, so it may not be able to intercept all missiles. What should I do? Build more systems
! It's easy for you to say, but what about the cost? Cost is a big problem. So I come here for help. Please help me to calculate the minimum number of interception systems needed.

Input

Input several sets of data. Each set of data includes: the total number of missiles (positive integers), and the altitude of the missiles (the altitude data given by the radar is a positive integer not greater than 30000, separated by spaces)

Output

Corresponding to each set of data output, how many sets of such missile interception systems should be equipped at least to intercept all missiles.

Sample Input

8 389 207 155 300 299 170 158 65

Sample Output

2

 

 

Idea one

The least interception system is actually a classic template question of the longest increasing subsequence. For the data given in the question, we traverse in turn, first initialize the first number to 1, because the LIS ending with the first number is 1, we Traversing backwards, at the same time of each traversal, we are in the current number num [ i ] num[i]In front of n u m [ i ] , look for a number smaller than it but it is a small number insidef [ i ] f[i]f [ i ] the largest number (f [ i ] f[ i ]f [ i ] representswhether [ i ] is [ i ]The number of elements of the longest increasing subsequence at the end of n u m [ i ] ), if found, we will add one to the LIS ending with the current number, and update the value of ans at the same time. At the end of the loop, ans is the value we requested. (The question of the longest increasing subsequence will be added in front of this if there is time)
Take the example as an example:
insert image description here

The number of elements in the longest increasing subsequence is 2, so the result is that 2 systems are required.
Intuitively, we can prove why it is the length of the longest ascending subsequence, and we can find that each element in the longest ascending subsequence must not be in an interception system, so at least so many are needed.
Let me explain why the essence is to find the longest ascending subsequence
According to the meaning of the question, we can analyze and know that what this question is essentially seeking is to divide it into at least several "longest non-ascending sequences"! That is:
How do you seek "the number of chains" without ascending "chains"?
The mathematical principle of this is the Dilworth theorem. If you are interested, you can take a look. The conclusion of the theorem is: for a partially ordered set, the minimum number of chain divisions is equal to the longest anti-chain length.
Therefore, the conclusion of this question can be obtained: Find the length of the longest ascending subsequence!
 
 

Code

#include <iostream>
#include <cstdio>
#include<cstring>
using namespace std;

int a[1005], dp[1005];
int main()
{
    
    
   
    int n, ans = 0;
    while(scanf("%d", &n) != EOF){
    
    
        ans = 0;
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; ++i) //扫描找最大上升子序列
        {
    
    
            int ma = 0; //存前面a[i]小的位置的最大的dp[j]值
            for (int j = 1; j < i; ++j)
            {
    
    
                if (a[j] < a[i])
                    ma = max(ma, dp[j]);
            }
            dp[i] = ma + 1; //个数加上自身的一个
            ans = max(ans, dp[i]);
        }
        printf("%d\n", ans);
   }
    return 0;
}

 
 

Idea two

In addition to the above DP approach, it is easy to think of a greedy algorithm.
We can be greedy like this:
Greedy process:
Scan each number from front to back, for each number:
Case 1: If the end of the existing subsequence is less than the current number, create a new subsequence;
Case 2: Put the current number in Go to the end of the smallest subsequence that is greater than or equal to it; (It is easy to understand, if there are many sequences that can be placed for you, let you put them at the end, and the subsequence with the smallest end has less impact and can give more opportunities later )
can be proved by the adjustment method that this kind of greedy can indeed get the optimal solution.
 
 

Code

#include <sstream>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100005;

int n;
int h[N], q[N];//q数组存储现有所有子序列的结尾

int main()
{
    
    
    while(cin >> n){
    
    
        for(int i = 0; i < n; i++) cin >> h[i];
        int cnt = 0; //当前子序列个数
        for (int i = 0; i < n; i ++ ) //从前往后贪心
        {
    
    
            int k = 0; //从前往后找的序列
            while (k < cnt && q[k] < h[i]) k ++ ;
            q[k] = h[i];
            if (k == cnt) cnt ++;
        }
        printf("%d\n", cnt);
    }
    return 0;
}

 
There is another greedy strategy
1. Find the first unmarked point from left to back,
2. Use this point as the highest point mark, and mark all non-strict single-point decreasing points from left to right
3. Until all Points are not marked
4. The final result is the number of highest points

This method is essentially similar to the above method, the difference is that here is to find "the subsequence with the most front starting point" every time, and the above is to find the subsequence with the smallest end each time, but it can be proved by mathematical induction that the closer the starting point is The subsequence before the subsequence must be smaller at the end.
 

Code

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1005;
int n,m;
int vis[N];
int h[N];

int main(){
    
    
    while(cin >> n){
    
    
    	memset(h, 0, sizeof(h));
    	memset(vis, 0, sizeof(vis));
	    for(int i = 1; i <= n; i++) cin >> h[i];
	    int cnt = 0;
	    for(int i = 1; i <= n; i++){
    
    
	        if(vis[i] == 1)continue;//如果该点被访问过,则continue
	        int t = h[i];//没有访问过则以该点为最高点,标记所有单调递减的导弹
	        vis[i] = 1;
	        cnt++;
	        for(int j = i + 1; j <= n; j++){
    
    
	            if(vis[j] == 1) continue;
	            if(h[j] <= t){
    
    //标记所有非严格单调递减的导弹
	                vis[j] = 1;
	                t = h[j];
	            }
	        }
	    }
	    cout << cnt << endl;
    }
    return 0;
}

HDU 1421 Moving Dorms

Link: http://acm.hdu.edu.cn/showproblem.php?pid=1421

Problem Description

Moving the dormitory is very tiring, xhd has a deep understanding. The time goes back to July 9, 2006, when xhd had no choice but to move from building 27 to building 3 because building 10 was going to be closed. Looking at the dormitory n items, xhd started to be in a daze, because n is an integer less than 2000, which is too much, so xhd decided to just move 2 k items. But it will still be very tiring, because 2 k is not small, it is not a big deal An integer greater than n. Fortunately, based on years of experience in moving things, xhd found that the fatigue degree of each move is proportional to the square of the weight difference between the left and right hands (here to add a sentence, each time xhd moves two things, the left hand one For example, if xhd holds an item with a weight of 3 in his left hand and an item with a weight of 6 in his right hand, then his fatigue degree after moving this time is ( 6 − 3 ) 2 = 9 (6-3)^2= 9(63)2=9. Now the poor xhd wants to know what the best state is after moving these 2*k items (that is, the lowest fatigue), please tell him.

Input

Each set of input data has two lines, the first line has two numbers n, k (2<=2*k<=n<2000). The second line has n integers representing the weight of n items (the weight is a A positive integer less than 2^15).

Output

Corresponding to each set of input data, there is only one output data representing his least fatigue, each row.

Sample Input

2 1
1 3

Sample Output

4

train of thought

For one operation, it is obvious that the closer the weight of the items to be lifted, the better. Is it possible to be greedy? Obviously, you can’t be greedy for the closest item to choose, so the overall situation cannot be satisfied. For example, there are only four numbers: 1 4 5 8, and you need to move it twice , if you choose 4 5 and 1 8, it is obviously not as good as 1 4 and 5 8.
According to the first feeling, is it true that the items that are lifted each time must be items with adjacent weights? It can be proved that this choice is indeed optimal.
Assuming four numbers from small to large: a, b, c, d, just prove the following expression:
( a − b ) 2 + ( c − d ) 2 < ( a − c ) 2 + ( b − d ) 2 (ab)^2+(cd)^2<(ac)^2+(bd)^2(ab)2+(cd)2<(ac)2+(bd)2
( a − b ) 2 + ( c − d ) 2 < ( a − d ) 2 + ( b − c ) 2 (a-b)^2+(c-d)^2<(a-d)^2+(b-c)^2 (ab)2+(cd)2<(ad)2+(bc)2
It is easy to know that the above formula is established (it can be expanded)
, so the first thing to do is to sort it first.

With the above conclusions, we continue to analyze: (The prerequisites are all arranged in order) The
fatigue of choosing a pair of 2 items is the least, and the conclusion is obviously
the least fatigue of choosing a pair of 3 items. Considering two adjacent ones, the answer is to
choose one of 4 items For the least fatigue, this is more general, and can be divided into two cases, either including the fourth item, or not including the fourth item (that is, only need to look at its sub-questions, look at the first three).
This way it is general.
It can be extended to select a pair of n items, and it is also divided into either including the nth item (that is, selecting the n-1th item and the nth item), or not including the nth item.

Next, let’s look at n items and choose two pairs (that is, move two times).
We can know that four items are moved twice, that is, we choose two adjacent ones
and then look at five items and move them twice, which means that the fifth item is included (that is, Choose the fourth and fifth as one trip) and compare the situation that does not include the fifth item (that is, the optimal solution for the first four to move two trips).
In this way, it can be extended
to move n items twice, divided into nth items (that is, n-1th and nth items) and nth items not included

By analogy, select k pairs of n items ( n ≥ kn≥knk

We consider dp [ n ] [ k ] dp[n][k]d p ​​[ n ] [ k ] means selecting 2 ∗ k 2*kfrom the first n items2The minimum fatigue of k items to express each state.
So solve eachdp [ i ] [ j ] dp[i][j]When the value of d p [ i ] [ j ]
for theiiitem i , if item iii item
thendp [ i ] [ j ] = dp [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j]dp[i][j]=dp[i1 ] [ j ] , provided that2 ∗ j ≤ i − 1 2*j ≤ i -12ji1
otherwise if selected then itemiii item, then the i-1th item has the smallest difference with it, then i− 1 i-1 itemi1 item Depends on the selection
dp [ i ] [ j ] = dp [ i − 2 ] [ j − 1 ] + ( a [ i ] − a [ i − 1 ] ) ∗ ( a [ i ] − a [ i − 1 ] ) dp[i][j] = dp[i-2][j-1] + (a[i]-a[i-1])*(a[i]-a[i-1] )dp[i][j]=dp[i2][j1]+(a[i]a[i1])(a[i]a[i1 ] )
of whicha [ i ] a[i]a [ i ] means the weight.
As long as the min() of the two is taken, the state transition equation is:dp [ i ] [ j ] = min ( dp [ i − 1 ] [ j ] , dp [ i − 2 ] [ j − 1 ] + ( a [ i ] − a [ i − 1 ] ) ∗ ( a [ i ] − a [ i − 1 ] ) ) dp[i][j] = min(dp[i-1] [j], dp[i-2][j-1] + (a[i] - a[i-1]) * (a[i] - a[i-1]))dp[i][j]=min(dp[i1][j],dp[i2][j1]+(a[i]a[i1])(a[i]a[i1]))

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;

int a[2010],dp[2010][1010]; //dp[i][j]表示有i个物品,搬j趟
int main(){
    
    
    int n, k;
    while(scanf("%d%d", &n, &k) != EOF){
    
        
        for(int i = 1; i <= n; i ++)
            scanf("%d", &a[i]);
        sort(a + 1, a + n + 1); //先按升序排列
        for(int i = 1; i <= k; i ++)
            for(int j = 2 * i; j <= n; j ++){
    
    
                dp[j][i] = dp[j - 2][i - 1] + (a[j] - a[j - 1])*(a[j] - a[j - 1]); //选第j件,所相邻的也选
                //这里加不加if其实无所谓(指if语句,后面求min还是要的),加了可以少算一点,因为j==i*2只有一种方案,全部都得搬。
                if(j != i * 2) dp[j][i]=min(dp[j][i], dp[j - 1][i]); //选第j件和不选第j件两种结果中取最小   
            }
        printf("%d\n", dp[n][k]);        
    }
    return 0;
}

The dp array here can actually only open dp [ 3 ] [ 1005 ] dp[3][1005]d p ​​[ 3 ] [ 1 0 0 5 ] , because dp[i][j] will only use dp[i-1][] and dp[i-2][] at most, for dp[i-3] [] and the previous data are not referenced, that is to say, these data are invalid, so you can only open three and use the technique of rolling the array to recycle the array.
Then change the expression of state calculation:
dp [ i % 3 ] [ j ] = min ( dp [ ( i − 1 ) % 3 ] [ j ] , dp [ ( i − 2 ) % 3 ] [ j − 1 ] + ( a [ i ] − a [ i − 1 ] ) ∗ ( a [ i ] − a [ i − 1 ] ) ) dp[i\%3][j] = min(dp[(i-1 )\%3][j], dp[(i-2)\%3][j-1] + (a[i] - a[i-1]) * (a[i] - a[i- 1]))dp[i%3][j]=min(dp[(i1)%3][j],dp[(i2)%3][j1]+(a[i]a[i1])(a[i]a[i1]))
 
 

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
int a[2010],dp[3][1010]; 
int main(){
    
    
    int n,k;
    while(scanf("%d%d", &n, &k)!=EOF){
    
        
        for(int i = 1; i <= n; i ++)
            scanf("%d", &a[i]);
        for (int i = 0; i <= 2; ++i){
    
    
            for (int j = 1; j < k + 1; ++j){
    
    
                dp[i][j] = INF;
            }
            dp[i][0] = 0;
        }
        sort(a + 1,a + n + 1); //先按升序排列
        for(int j = 2; j <= n; j ++){
    
    
            for(int i = 1; i <= k; i ++){
    
    
                dp[j % 3][i]=min(dp[(j - 1) % 3][i], (dp[(j - 2) % 3][i - 1] + (a[j] - a[j - 1]) * (a[j] - a[j - 1]))); //选第j件和不选第j件两种结果中取最小   
            }
        }
        printf("%d\n",dp[n%3][k]);        
    }
    return 0;
}

Guess you like

Origin blog.csdn.net/xxmy7/article/details/113574013