01 Backpack problem, simple AC code with detailed explanation, palace treasure hunting, fluctuation series and other DP problems.

01 Backpack problem

Title Description

There are N items and a backpack with a capacity of V. Each item can only be used once.

Item i has a volume of vi and a value of wi.

Solve which items are loaded into a backpack so that the total volume of these items does not exceed the backpack volume and the total value is greatest. Output maximum value.

Input Format

The first two integers, N and V, are separated by spaces to indicate the number of items and the backpack area.

Next, there are N lines, two integers vi,wi in each line, separated by spaces, representing the volume and value of item i I

Output Format

Output an integer representing the maximum value.

Two ways to solve this problem, one-dimensional and two-dimensional

Two-dimensional:

[External chain picture transfer failed, source may have anti-theft chain mechanism, it is recommended to save pictures and upload them directly (img-Q9ktruph-1633763) (C:UsersASUSPicturesBlog Pictures20101740701.png)]

#include<iostream>
using namespace std;
int v[1000],w[1000],f[1000][1000];
//f[][] Saves the best data for each time, just like a map
int main()
{
    int N,V;
    cin>>N>>V;
    //Start from 1 to prevent overrun
    for(int i=1;i<=N;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=V;j++)
        {
 //f[i][j] has two possibilities, i f the current judgment does not fit
  //Then f[i][j] takes the value of its previous line.
 //If you can load it, make a selection with max
           
            f[i][j]=f[i-1][j];
            if(j>=v[i])
                f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
          //Here we can see f[i-1][j-v[i]]+w[i]. Maybe you don't understand this operation here. We split it up, which means: [i-1] corresponds to the previous line in the diagram, and [j-v[i]] means the total capacity of the backpack minus the maximum remaining capacity of the current item to hold the most valuable item. You can view it from the picture and add w[i], This also paves the way for the next one-dimensional solution.
        }
    }
    cout<<f[N][V];//In this way, it must be that the lowest right corner of a two-dimensional array is the maximum.
    
    system("pause");
    return 0;
}

One-dimensional approach:

Two-dimensional is different from one-dimensional, that is, two-dimensional is pushed forward from the back. Why can you do this? You can imagine that in the two-dimensional solution, the top and left sides are both 0. The second line has the same number except the first number is zero, so one-dimensional can go back and forward.

Let's talk about why we need to move backwards and forwards, because if we go there will be a phenomenon that overwrites the last data. So you have to reason backwards and forwards.

#include <iostream>
using namespace std;
int v[1000],w[1000],f[1000];
int main()
{
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=N;i++)
        for(int j=V;j>=v[i];j--)
            //Explain why j>=v[i], because if j<v[i] is the time when this item is not loaded and does not specify the maximum or the last time it was loaded, the time value of j<[i] is unchanged and should not be considered.
        {
            f[j]=f[j]//This step can be ignored, just like 2-D
                f[j]=max(f[j].f[j-v[i]]+w[i]);
        }
    cout<<f[V];//Why is f[V] here, because according to this idea, the last digit must be the largest!
    system("pause");
    return 0;
}

Peanut picking problem

The key idea is that the title requires that you only go down or right at a time, so we will compare the best solution for going down and right. Here you might want to point out that if the overall trend is in the lower right direction, the lower left value will not be ignored? That's not the case, because this two-dimensional array traverses the best solution everywhere. See the core code below.

 for(int i=1;i<=r;i++)
        {
            for(int j=1;j<=c;j++)
            {
                map[i][j]+=max(map[i-1][j],map[i][j-1]);
            }
        }

Let's parse this code. A two-level for loop ensures that the loop checks all areas of the map needed, and each time it decides whether to go right or down, the current position has the maximum value,'+='means you need to add its own value

In fact, this core code should also take into account the special case of the first line and the first column, since they are both zero, but there is no need to spend that time discussing them. It is more straightforward to use this line of code directly.

Next comes the full code.

#include <iostream>
using namespace std;
int map[1000][1000];
int main()
{
    int N;
    cin>>N;
    while(N--)
    {
       	int r,c;
        cin>>r>>c;
        for(int i=1;i<=r;i++)
        {
            for(int j=1;j<=c;j++)
            {
                cin>>map[i][j];
            }
        }
        for(int i=1;i<=r;i++)
        {
            for(int j=1;j<=c;j++)
            {
                map[i][j]+=max(map[i-1][j],map[i][j-1]);
                //The main role of this code is illustrated above.
            }
        }
        cout<<map[r][c];
        //It must be the lower right corner, that is, the boundary value is the largest.
        cout<<endl;
    }
    
    system("pause");
    return 0;
}

Longest Subsequence

For example, find the longest ascendent sequence of 271 5 6 4 3 8 9

The first number d(1)=1 subsequence is 2;

Before the first two numbers 7, there are 2 sub-sequences less than 7 d(2)=d(1)+1=2 which are 27

None of the first three numbers preceded 1 is smaller than 1, and 1 itself consists of a subsequence d(3)=1 subsequence 1

The first four number 5 preceded by 2 sub-sequences less than 5 d(4)=d(1)+1=2 was 25

The first 5 numbers 6 precede 25 sub-sequences less than 6 d(5)=d(4)+1=3 to 25 6

The first six numbers 4 precede 2 sub-sequences less than 4 d(6)=d(1)+1=2 to 24

Before the first 7 numbers 3, there are 2 sub-sequences less than 3 d(3)=d(1)+1=2 which are 23

The first eight numbers are preceded by 256 sub-sequences less than 8 d(8)=d(5)+1=4 which are 256 8

The first 9 numbers preceded by 2568 sub-sequences less than 9 d(9)=d(8)+1=5 are 256 8 9

#include <iostream>
using namespace std;
int a[1000];//Store the value of each number in the subsequence.
int f[1000];//Record the largest subsequence of the current number that meets the criteria
int main()
{
    int N;
    int ans=0;
    cin>>N;
    for(int i=1;i<=N;i++)
    {
        cin>>a[i];
        f[i]=1;
    } 
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(a[j]<a[i])
            f[i]=max(f[i],f[j]+1);
        }
    }
    //The two for loops above are the core code for this topic:
    //By looking at the examples on this topic, i believe you already have some ideas of your own. The first for loop is to check the numbers in each sequence, and the second for loop is to loop all the numbers before the current number i.
    //The purpose of f[i]=max(f[i],f[j]+1) is to filter out the largest subsequence of the current number that meets the criteria.
      for(int i=1;i<=N;i++)
         ans=max(ans,f[i]) ;
    //This for loop is also important because unlike the two questions above, the maximum value is at the end, so you need to loop through the entire f[] array to find the maximum value.
                     cout<<ans;
    system("pause");
    return 0;
}

Ant cold problem

Ideas for this topic:

The need to understand this question is that when two ants meet, they will reverse each other. It is helpful to understand this process as two ants pass through each other.

Generally speaking, the title says the first data is the cold ant. If the ant is on the left side of the cold ant and the ant is going to be infected in the direction of this cold ant, the same is true. Ants on the right side of this cold ant will be infected as long as they are walking towards this cold ant.

Special case: This cold ant (that is, the first data, is on the boundary, such as the leftmost side, and it is walking in the direction of the left side) will not be infected even if it is walking in the direction of the cold (at this time the left), the same is true for the right side.

#include <iostream>
#include <cmath>
using namespace std;
int a[1000];
int main()
{
    int N;
    int left=0,right=0;
    cin>>N;
    for(int i=0;i<N;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<N;i++)//(0 is for judgment, not for consideration)
    {
        if(fabs(a[i])<fabs(a[0]))
      //It's important to use Fabs for both absolute values here, ignoring fabs(a[0]) the first time you write it, and later finding that the first data may also be negative. So it must be absolute to make a judgment.
        {  if(a[i]>0)
       //If fabs(a[i])<fabs(a[0]) indicates that the ant must be on the left side of the cold ant, then judge the direction of the ant's walk. The same is true below.
              left++;
        }
        else
            if(a[i]<0)
                right++;
    }
    cout<<right+left+1;
    system("pause");
    return 0;
}

Number not available

Ideas:

One of the main points of this question is to find an upper limit, which is the smallest common multiple of the two numbers entered. The example given in the title is 4 and 7, then the upper limit is 28;

#include <iostream>
using namespace std;
int main()
{
    int n,m,h;
    int flag=0;
    cin>>n>>m;
      int Max=m*n;
    for(int k=Max;k>=1;k--)
    {
       	for(int i=0;i<Max;i++)
    	{
      	  for(int j=0;j<Max;j++)
       		 {
          		  if(i*n+j*m==i)
              		  flag=1;
       		 }
    	}
     if(flag==0)
        {
           h=k;
           break;
        }
        flag=0;
        
    }
    cout<<h;
    system("pause");
    return 0;
}

Palace Treasure Search (two ways of solving problems, roughly the same, to see which is easy for you to understand)

Solving ideas:

The title said that if you encounter something larger than the current maximum value (so the current maximum value items must be recorded with a variable to facilitate subsequent comparisons), you can take it or not (so there are two cases, take or not); It's a great question to use recursion, which can be understood as taking into account every possible coordinate (in each case) on a map and making a comparison. Code directly below.

#include <iostream>
using namespace  std;
int n,m,k,Count;		//Count records the number of times a condition is currently met and is the value of the final output.
int a[1000][1000];			
void dfs(int i, int j, int p, int cnt)		//p represents the maximum value of all items and cnt represents the current number of items in your hand.
{
	if (i == n - 1 && j == m - 1)		//No matter what path you take, you will not be able to judge the result until you reach the bottom right corner.
	{
		if (cnt == k || cnt == k - 1 && a[i][j] > p)	
            //There are two possible judgments at the end
            //1: The number of items in your hand is already the k required by the title.
            //2: The items in your hand are only k-1, but the value of the last lattice's treasure is greater than the current maximum value p, which also meets the criteria.
			Count++; 
        //return; You don't want to use this sentence here, because you have a mandatory if statement with a bounding constraint below it.
	}
	if (i < n)//Go Down
	{
		if (a[i][j] > p)	//There are two situations to go down, even if the treasure is larger than the current maximum, you can choose to take it or not.
			dfs(i + 1, j, a[i][j], cnt + 1);		//This is the case with Grab
		dfs(i + 1, j, p, cnt);	
    //This statement is taken regardless of the condition, so it must not be in an if statement, and cannot be added {}; If you add it, it becomes the case that you don't take it until the condition is met, but ignore the case when the condition is not met.
	}
	if (j < m)//Go right. Same as above.
	{
		if (a[i][j] > p)
			dfs(i, j + 1, a[i][j], cnt + 1);
		dfs(i, j + 1, p, cnt);
	}
}
int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> a[i][j];
		}
	}
	dfs(0, 0, -1, 0);//Also note here that the -1 is here, because the value of the treasure may be 0, it must start with -1, otherwise the map can't take into account every situation.
	cout << Count;
	system("pause");
	return 0;
}

Below is the second way of understanding, much the same as little.

#include <iostream>
using namespace  std;
int n, m, k, Count;
int a[1000][1000];
void dfs(int i, int j, int p, int cnt) p Represents the maximum value in all items, cnt Represents the current number of items in your hand.
{
	if (i >= n || j >= m)			//Unlike the above criteria, return occurs when the boundary is exceeded.
		return;
	if (i == n - 1 && j == m - 1)	//You have to go to the bottom right corner to make your judgment.
	{
		if (cnt == k || cnt == k - 1 && a[i][j] > p)
			Count++;
		return;
	}

	if (a[i][j] > p)  //The idea here is the same as above, and our goal is to take every situation into account on the map, because it is a recursive approach, so every coordinate situation can be considered.
        //You can think of these two dfs in the if statement as walking under conditions. (that is, if you can pick up a treasure, you can)
	{
		dfs(i + 1, j, a[i][j], cnt + 1);
		dfs(i, j + 1, a[i][j], cnt + 1);
	}
    //These two dfs ignore all the treasures on the map and just walk
	dfs(i + 1, j, p, cnt);
    dfs(i, j + 1, p, cnt);
    //These four dfs must be able to iterate through each case. (recursive memory traversal)
}
int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> a[i][j];
		}
	}
	dfs(0, 0, -1, 0);			
	cout << Count;
	system("pause");
	return 0;
}

Wave series

It really takes time for you to understand the details.

Ideas:

As shown in the following figure, the first item of the sequence is x, the tolerance is set, set={a, -b};

From the first image below, we can see that the number of set s is n*(n-1)/2 (formula: first plus last divided by 2)

We let cnt=n*(n-1)/2 (i.e. the number of sets) use these analyses to show that the following conditions are satisfied: sequence sum=Sn=nx+cnt set

We assume that the number of a is num1, because the sum of a and B is cnt, then the number of B is cnt-num1. An expression nx+a * num1-b(cnt-num1)=s can be derived. Convert the location to n * x=s-anum1+b(cnt-num1);. As we have said above, the maximum number of set tings is n(n-1)/2. Sets have two possibilities, A and B. If all is a, that is, a maximum of n*(n-1)/2 a (at this point 0 b), then knowing this formula is easy because there are only two unknown numbers x and num1 in the formula, and we have said that the upper limit of num1 (that is, the number of a) is n*(n-1)/2. We just need to enumerate this n*(n-1) /2 Do a for loop to find the value that meets the criteria, and you will know the value of X when you know the num1 that meets the criteria.

    int ans=0;//Used to record the number of times a condition has been met.
	int cnt=n*(n-1)/2;		//The sum of quantities a and b.
    for(int i=0;i<=cnt;i++)	//This is to enumerate all possible quantities of a starting from 0. As long as x is an integer, a of this number is qualified at this time.
    {
        long long h=s-i*a+(cnt-i)*b;
        //First of all, you need to know what this code means. Combining the formula above, you can see that h here represents even n*x; that's why the lower if judges
        if(h%n==0)//This judgment means to express that as long as x is an integer, the number of a at this time is sufficient.
            ans+=f[i];//Here, f[i] will be described below. Anyway, just know that the number of I in this for loop at this time is the number of A. f[i] indicates that the current number of a satisfies the combination. Maybe you can't see what combination I'm talking about here. I'll introduce it below.
    }

Sequence NumberCorresponding Value
0x
1x+set
2x+set+set
......
n-1x+(n-1)*set

Situation 1:

Sequence NumberCorresponding Value
1x
2x+a
3x+a-b
4x+a-b+a
5x+a-b+a+a
The sum5x+7a-3b

The number of a is 7, which is a 7 made up of 4+2+1

Situation 2:

Sequence NumberCorresponding Value
1x
2x+a
3x+a+a
4x+a+a-b
5x+a+a-b-b
The sum5x+7a-3b

The number of a is 7, but it is composed of 4+3, and the corresponding number of b is 3, which is composed of 2+1.

Look at these two situations. Although they are all seven a's, they have different possibilities of arrangement. The most important detail is here. Although we can find the number of a by that step over there, the combination of a is not known yet. The possibility of all problems changing to the combination of a is shown in the code above.

#include <iostream>
using namespace std;
long long n,s,a,b;
int f[100];//Its subscript is the number of a, assuming a=7, f[7] represents how many possibilities can be made up of a number when it is 7. It can be 4+3 or 4+2+1, as described above.

//When we talk about the implementation of the function below, some students may not understand it. It is very similar to the idea of 01 knapsack problem. All of them use the idea of sliding array. You can see the explanation of 01 knapsack problem above. If you master it, you can be sure it will help your programming ability.

//The dynamic programming below is achieved directly by converting two-dimensional ideas into one-dimensional ones. If you don't understand it, you can see the explanation of my 01 backpack problem.
//Let's first simulate a two-dimensional explanation. Let's use the two-dimensional array f[i][j], which converts the two-dimensional array f[][] directly to the one-dimensional array f[].
//Before you start, in case you are confused about the function of the two-dimensional array f[][], here you can see that it has nothing to do with the number of A. After the number of a is calculated, you can use this array to analyze how the number of a can be combined. For example, when the number of a is 7, how many possibilities can it be composed. It can be 4+3 or 4+2+1.
//Okay, then formally explain what I and j represent in f[i][j]. I represents the possible number of a at present, remember that only a, not b (can be 1, 2, 3, 4...). It starts with 1 and j represents the sum of the values of the first I.
//First of all, the value of point f[i][0] must be set to 1. It's very detailed here, and I'll show you a few examples below.
//   f[i][j]=f[i-1][j], when j<i.
//	f[i][j]=f[i-1][j]+f[i-1][j-i], when j>=2.
//Let me elaborate on the true meaning of i, for example, f[3][2]. It means that any array selected in numbers 1, 2, 3 can make up the final sum (j). Here the corresponding sum (j) is 2, which can make up 2. Only one case is to select 2 directly, and all f[3][2]=1 (note in this case that f[3][2] is j<i, using the formula f[i][j]=f[i-1][j] For example, f[4][2], which means that any number can be selected from the four numbers 1,2,3,4 to get 2. There is only one possibility, that is, to directly select 2, so f[4][2]=1. (Note at this point that f[4][2] is j<i, and the formula used is f[i][j]=f[i-1][j]. And f[3][3], which means to select any number from the three numbers 1,2,3, where the corresponding sum (j) There are two ways to make up 3, choose 1 and 2, or choose 3 directly, so f[3][3]=2 (note that f[3][3] is j>=i, so the formula used is f[i][j]=f[i-1][j]+f[i-1][j-i]). We bring in the specific number as f[3][3]=f[2][3]+f[2][0]. Note i f f[3][0] appears here, that is, the f[i][0] mentioned above This is why we set f[i][0] to 1, because j=i at this time, so f[2][0] appears. Let's take a deep look at the meaning of f[3][3]=f[2][3]+f[2][0]. The left part f[3][3] needs to be superimposed on the right part (that is, on the last state). This expression's f[2][3] Represents finding a combination of 3 in 1,2, that is, 1+2, where both numbers are selected. The meaning of this part of f[2][0] is that the left hand side is now f[3][3], from the original 1,2 two numbers, to the present 1,2,3 numbers, and 3 numbers are selected (key understanding), so the function of f[2][0] is to judge, after 3 has been selected. (Important understanding), and then choose from the numbers 1 and 2 that satisfies 0, which also explains why all f[i][0] should be set to 1. Okay, I can only say this, as for how to convert two-dimensional f[i][j] to one-dimensional f[i] Refer to the 01 knapsack problem I mentioned, I strongly recommend that you understand these two topics, which will be very helpful for you to understand dynamic planning!!
void dp(int x)		
{
    f[0]=1;
    for(int i=1;i<x;i++)	//To briefly explain why it is i<x, not i<=x, see the table below. A starts with serial number 2, so I'll have a good understanding.

//|Serial Number|Corresponding Value|
//| :--: | :-------- |
//|  1   | x         |
//|  2   | x+a       |
//|  3   | x+a+a     |
//|  4   | x+a+a-b   |
//|  5   | x+a+a-b-b |
//| Sum | 5x+7a-3b |
    {
        for(int j=i*(1+i)/2;j>=i;j--)
        {
            f[j]=f[j]+f[j-i];
        }
    }
}

Full code:

#include <iostream>
using namespace std;
long long n,s,a,b;
int f[100];
void dp(int x)		
{
    f[0]=1;
    for(int i=1;i<x;i++)
    {
        for(int j=i*(1+i)/2;j>=i;j--)
        {
            f[j]=f[j]+f[j-i];
        }
    }
}
int main()
{
   cin>>n>>s>>a>>b;
    dp[n];
    int ans=0;
    int num=n*(n-1)/2;
    for(int i=0;i<=num;i++)
    {
        long long h=s-i*a+(num-i)*b;
        if(h%n==0)
            ans+=f[i];
    }
    cout<<ans;
    system("pause");
    return 0;
}

Tags: C++ data structure Back-end

Posted on Sat, 20 Nov 2021 16:16:09 -0500 by supinum