Asymmetric DP

table of Contents

One, symmetric DP, asymmetric DP

1. Symmetrical DP

2. Asymmetric DP

Second, the actual OJ combat of asymmetric DP

CSU 1207: Strictly-increasing sequence

HDU 1176 Free Pie

POJ - 1390 Blocks

POJ 3661 Running


One, symmetric DP, asymmetric DP

This is a new concept I put forward based on my understanding and induction of DP.

1. Symmetrical DP

Sequence DP  https://blog.csdn.net/nameofcsdn/article/details/112771904

Interval DP  https://blog.csdn.net/nameofcsdn/article/details/112981922

These two types of DP are actually relatively simple, because the object space is equal to the solution space  https://blog.csdn.net/nameofcsdn/article/details/111885131

If the problem requires f(i,j), then the recurrence in the algorithm will be f(i,j)=......

Because i and j here are the same concept, the status is symmetric, and because the status of the object space and solution space is symmetric, I call it symmetric DP

2. Asymmetric DP

There are some DP problems. For example, the problem is abstracted into f(n). The recurrence of f is very complicated and may not even exist.

But we can introduce an auxiliary concept, take a variable i in the corresponding auxiliary space, and get the sub-problem g(n,i)

Calculate its value according to the recurrence formula of g(n,i), and finally calculate f(n) according to f(n)=h({g(n,i)|i∈A}) ,

Among them, h represents how to obtain the solution of the original problem according to the solution integration of the sub-problems, generally sum function or max function or min function, and A represents auxiliary space

Because the object space where n is located and the auxiliary space A together constitute the solution space, that is, the object space and the solution space are different, and because n and i are not the same concept, I call it asymmetric DP

 

Second, the actual OJ combat of asymmetric DP

CSU 1207: Strictly-increasing sequence

topic:

Description

If any item in a sequence is greater than the previous item, then we call this sequence a strictly increasing sequence.

Now there is a sequence of integers. You can merge several adjacent items in the sequence into one item. After the merge, the value of this item is the sum of the values ​​of the items before the merge. After merging several times, a strictly increasing sequence must be obtained in the end. How many items can the obtained strictly increasing sequence have at most?

Input

The first line of input data contains a positive integer T (1 <= T <= 200), indicating that there will be a total of T groups of test data.

The first line of each group of test data contains an integer N (1 <= N <= 1000), indicating that there are N items in this integer sequence. The next line contains N positive integers not greater than 10^6, describing the values ​​of each item in this sequence in turn.

At most 20 sets of data satisfy N> 100.

Output

For each set of test data, use one line to output an integer, indicating the maximum number of items in the final strictly increasing sequence.

Sample Input

3
2
1 1
3
1 2 3
5
1 3 2 6 7

Sample Output

1
3
4

Idea: Interval DP

dp[i][j] represents the number of j from 1 to j, and the value of the optimal solution when the last segment is from i to j (may be 0)

Code:

#include<iostream>
using namespace std;
 
int dp[1001][1001];//dp[i][j]表示最后一段为i到j的最优解
 
int main()
{
	int T, n, num[1001], sum[1001];
	cin >> T;
	while (T--)
	{
		cin >> n;
		sum[0] = 0;
		for (int i = 1; i <= n; i++)
		{
			cin >> num[i];
			dp[1][i] = 1, dp[i][i - 1] = 0;
			sum[i] = sum[i - 1] + num[i];
		}
		for (int i = 1; i < n; i++)
		{
			int ki = i;
			for (int j = i + 1; j <= n; j++)
			{
				dp[i + 1][j] = dp[i + 1][j - 1];
				while (ki)
				{
					if (sum[i] - sum[ki-1] >= sum[j] - sum[i])break;
					if (dp[ki][i] > 0 && dp[i + 1][j] < dp[ki][i] + 1)
						dp[i + 1][j] = dp[ki][i] + 1;
					ki--;
				}
			}
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)if (ans < dp[i][n])ans = dp[i][n];
		cout << ans << endl;
	}
	return 0;
}

HDU 1176 Free Pie

topic:

Description

It is said that there will be no pies in the sky, but one day the gameboy was walking on the path home, and suddenly a lot of pies fell from the sky. Said gameboy's character is really good, this pie did not fall anywhere else, it fell within 10 meters of his side. If the pie falls on the ground, of course it can't be eaten, so the gameboy immediately removed his backpack to pick it up. But because there is no one on both sides of the trail, he can only pick up on the trail. Because gameboy usually stays in the room to play games, although he is a very agile player in the game, his motor nerves are particularly slow in reality, and he can only catch falling pies within a range of no more than one meter per second. Now give the coordinates of this trail as shown in the icon: 
In order to simplify the problem, suppose that in the next period of time, the pie will fall in the 11 positions of 0-10. At the beginning, the gameboy stood at position 5, so in the first second, he could only receive the pie in one of the three positions 4, 5, and 6. How many pies can gameboy receive at most? (Assuming that his backpack can hold an infinite number of pies) 

Input

There are multiple groups of input data. The first line of each group of data is a positive integer n (0<n<100000), which means that n pies fell on this path. In the resulting n rows, each row has two integers x, T (0<T<100000), which means that a pie falls on point x at second T. Multiple pies may fall at the same point in the same second. When n=0, the input ends. 

Output

Each set of input data corresponds to one line of output. Output an integer m, indicating that gameboy may receive at most m pies. 
Tip: The amount of input data for this question is relatively large. It is recommended to use scanf to read in. Using cin may time out. 

Sample Input

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

Sample Output

4

This problem is obviously dynamic programming, which can be understood as a DP problem with 11 arrays, which is equivalent to an enhanced version of spiral induction in mathematical induction.

Code:

#include<iostream>
#include<stdio.h>
using namespace std;
 
 
int n, x, t;
int num[100001][11];
 
int get(int i, int j)
{
	int r = num[i - 1][j];
	if (j && r < num[i - 1][j - 1])r = num[i - 1][j - 1];
	if (j < 10 && r < num[i - 1][j + 1])r = num[i - 1][j + 1];
	return r;
}
 
int main()
{
	while (scanf("%d", &n))
	{
		if (n == 0)break;
		for (int j = 0; j <= 100000; j++)
			for (int i = 0; i < 11; i++)num[j][i] = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d%d", &x, &t);
			if (x >= 5 - t && x <= 5 + t)num[t][x] ++;
		}
		for (int j = 1; j <= 100000; j++)
			for (int i = 0; i < 11; i++)num[j][i] += get(j, i);		
		int maxx = 0;
		for (int i = 0; i < 11; i++)
			if (maxx < num[100000][i])maxx = num[100000][i];
		printf("%d\n", maxx);
	}
	return 0;
}

No matter what the input is, the program does not need to be sorted.

This code can also continue to optimize the time. In many places, 100,000 is not needed. It depends on the input data to determine how big it needs to be.

But this is only 92ms AC, too lazy to change it.

One thing to note is that if one of the n pies entered is 3,1, the pies will never be received.

This situation needs to be distinguished, and the way to deal with it is to simply ignore it without counting.

The judgment condition is x >= 5-t && x <= 5 + t. Those that meet this are all pie that can be received, and those that are not satisfied are not.

POJ - 1390 Blocks

topic:

Some of you may have played a game called 'Blocks'. There are n blocks in a row, each box has a color. Here is an example: Gold, Silver, Silver, Silver, Silver, Bronze, Bronze, Bronze, Gold. 
The corresponding picture will be as shown below: 
If some adjacent boxes are all of the same color, and both the box to its left(if it exists) and its right(if it exists) are of some other color, we call it a 'box segment'. There are 4 box segments. That is: gold, silver, bronze, gold. There are 1, 4, 3, 1 box(es) in the segments respectively. 
Every time, you can click a box, then the whole segment containing that box DISAPPEARS. If that segment is composed of k boxes, you will get k*k points. for example, if you click on a silver box, the silver segment disappears, you got 4*4=16 points. 
Now let's look at the picture below: 
The first one is OPTIMAL. 
Find the highest score you can get, given an initial state of this game. 

Input

The first line contains the number of tests t(1<=t<=15). Each case contains two lines. The first line contains an integer n(1<=n<=200), the number of boxes. The second line contains n integers, representing the colors of each box. The integers are in the range 1~n.

Output

For each test case, print the case number and the highest possible score.

Sample Input

2
9
1 2 2 2 2 3 3 3 1
1
1

Sample Output

Case 1: 29
Case 2: 1

Code:

#include<iostream>
using namespace std;
 
int num[201], col[201], ans[201][201][201];//前面附加t个同色盒子
 
int dp(int l, int r,int t)
{
	if (l > r)return 0;
	if (l == r)return (num[l] + t) * (num[l] + t);
	if (ans[l][r][t])return ans[l][r][t];
	int a = dp(l, l, t) + dp(l + 1, r, 0);
	for (int i = l + 1; i <= r; i++)
	{
		if (col[i] != col[l])continue;
		if (a < dp(l + 1, i - 1, 0) + dp(i, r, t + num[l]))
			a = dp(l + 1, i - 1, 0) + dp(i, r, t + num[l]);
	}
	ans[l][r][t] = a;
	return a;
}
int main()
{
	int T, n;
	cin >> T;
	for(int i=1;i<=T;i++)
	{
		cin >> n;
		int c1 = -1, c2, key = 0;
		for (int i = 0; i <= n; i++)
		{
			num[i] = 0;
			for (int j = 0; j <= n; j++)for(int t=0;t<=n;t++)ans[i][j][t] = 0;
		}
		for (int i = 0; i < n; i++)
		{
			cin >> c2;
			if (c1 != c2)c1 = c2, col[++key] = c2;
			num[key]++;
		}
		cout << "Case " << i << ": " << dp(1, key, 0) << endl;
	}
	return 0;
}

POJ 3661 Running

topic:

Description

The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ≤ N ≤ 10,000) minutes. During each minute, she can choose to either run or rest for the whole minute.

The ultimate distance Bessie runs, though, depends on her 'exhaustion factor', which starts at 0. When she chooses to run in minute i, she will run exactly a distance of Di (1 ≤ Di ≤ 1,000) and her exhaustion factor will increase by 1 -- but must never be allowed to exceed M (1 ≤ M ≤ 500). If she chooses to rest, her exhaustion factor will decrease by 1 for each minute she rests. She cannot commence running again until her exhaustion factor reaches 0. At that point, she can choose to run or rest.

At the end of the N minute workout, Bessie's exaustion factor must be exactly 0, or she will not have enough energy left for the rest of the day.

Find the maximal distance Bessie can run.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 contains the single integer: Di

Output

* Line 1: A single integer representing the largest distance Bessie can run while satisfying the conditions.

Sample Input

5 2
5
3
4
2
10

Sample Output

9

For this problem, I have produced 4 ideas from beginning to end. The first 3 ideas were all dead, and the last one finally succeeded.

Idea one, super memory + timeout, no AC but the code is correct, but the efficiency is very low.

The second idea is that the code is wrong because the restriction conditions of the question cannot be met.

Idea three, overtime, the code is correct, but the efficiency is not high enough.

The fourth idea is completely correct.

 

Idea 1: Divide the time into many segments, each segment starts from the exaustion factor of 0, and ends with the exaustion factor of 0, find the maximum distance you can run.

sum[i][j] represents the maximum distance from i to j, to meet the above conditions.

Code:

#include<iostream>
#include<string.h>
using namespace std;
 
int list[10001];
int sum[10001][10001];
int a;
int n, m;
 
int f(int i, int j)		
{
	if (sum[i][j] >= 0)return sum[i][j];
	if (i >= j)return 0;
	if (i + 1 == j)return list[i];
	if (j - i + 1 <= m * 2 && (j - i) % 2)
	{
		sum[i][j] = 0;
		for (int k = i; k <= (i + j) / 2; k++)sum[i][j] += list[k];
	}
	for (int k = i; k < j; k++)
	{
		a = f(i, k) + f(k + 1, j);
		if (sum[i][j] < a)sum[i][j] = a;
	}
	return sum[i][j];
}
 
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> list[i];
	memset(sum, -1, sizeof(sum));
	cout << f(1, n);
	return 0;
}

After submitting it, I found out that the array of 10,000 by 10,000 was too large and over memory.

In fact, it is not just a memory issue, the time complexity of this idea is very high, and it is not desirable.

 

Idea 2: Finding the end of the i-th minute, if the exaustion factor is j, the maximum distance can be run.

sum[i][j] represents the maximum distance that can be run when any i does not exceed n, any j does not exceed m, and the end fatigue coefficient of the i-th minute is j.

Why do I want to emphasize arbitrary and profound meaning, please read down (in line 4 of idea three)

Only by being very clear about the state sum array defined by oneself, without any ambiguity, is it not easy to make mistakes.

Code:

#include<iostream>
#include<string.h>
using namespace std;
 
int list[10001];
int sum[10001][501];
int a,b;
int n, m;
 
int f(int i, int j)		
{
	if (sum[i][j] >= 0)return sum[i][j];
	if (i == 1)
	{
		if (j)return list[1];
		return 0;
	}
	if (j > 0)sum[i][j] = f(i - 1, j - 1) + list[i];
	if (j < m)
	{
		a = f(i - 1, j + 1);
		if (sum[i][j] < a)sum[i][j] = a;
		if (j == 0)
		{
			a = f(i - 1, 0);
			if (sum[i][j] < a)sum[i][j] = a;
		}
	}
	return sum[i][j];
}
 
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> list[i];
	memset(sum, -1, sizeof(sum));
	cout << f(n, 0);
	return 0;
}

There is one detail here that I am not sure about, that is, in sum[1][j], if j is greater than 1, it makes sense.

I think it is unrealistic that j in sum[1][j] is greater than 1, but it should have no effect on the optimal solution for our problem, but I am not very sure.

This code does not guarantee the limitation of the problem: once you start to rest, you must rest until the exaustion factor is 0 to continue running, and decisively give up.

 

Idea 3: sum[i][j] means the end of the i-th minute, if the exaustion factor is j, the maximum distance can be run.

But there is a restriction. If j is not 0, it represents the maximum point of the exaustion factor as a function of time.

In other words, f(6,3) indicates how far you can run if you are running in the 4th, 5th, and 6th minutes, and the exaustion factor is 0 after running.

Such a state is very interesting, instead of defining sum for any state that may be generated during running,

The sum is only defined at the maximum and zero points of the function of the exaustion factor with respect to time changes.

Please note that some sum[i][j] is not calculated, but not defined! The former is a slight difference in calculation methods, and the latter is the essential difference in thinking!

Code:

#include<iostream>
#include<string.h>
using namespace std;
 
int list[10001];
int sum[10001][501];
int a;
int n, m;
 
int f(int i, int j)
{
	if (i <= 0)return 0;
	if (sum[i][j] >= 0)return sum[i][j];	
	if (j == 0)
	{
		sum[i][j] = f(i - 1, 0);		//第i分钟在休息
		for (int k = i-m; k < i; k++)
		{
			a = f(k, i - k);
			//第k分钟在跑,到了顶点,然后开始休息,直到第i分钟结束才让疲劳度降为0
			if (sum[i][j] < a)sum[i][j] = a;
		}
	}
	else
	{
		a = 0;		//j不为0那么就是疲劳度的极大值点
		int ki = i;	//不需要进行任何的分类讨论,也不需要max函数之类的
		for (int kj = j; ki>0 && kj>0; ki--,kj--)a += list[ki];
		sum[i][j] = a + f(ki, 0);
	}
	return sum[i][j];
}
 
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> list[i];
	memset(sum, -1, sizeof(sum));
	cout << f(n, 0);
	return 0;
}

This code still times out.

Think about it carefully, mainly in the f function, when j is not 0, there is a loop.

Although there is no need for any discussion, the loop seems to be very short, but in the case of constant calling of f, the work of this loop is greatly repeated.

For example, running plan one, 1-20 minutes of running, 21-40 minutes of rest,

Running plan two, 1-19 minutes of running, 20-40 minutes of rest.

Obviously, program one is better than program two. When we thought about this comparison, we actually found that these two programs have high similarities.

For the two more arbitrary schemes, we cannot have this feeling.

Looking at the code above, calculating sum[20][20] and calculating sum[19][19], what did we find?

Gosh! Almost all the work (ie addition operation) is repeated!

Realizing the key to the problem, we are not far from the answer.

 

Idea 4: sum[i][j] means the end of the i-th minute, if the exaustion factor is j, the maximum distance can be run.

But there is a restriction. If j is not 0, it means the maximum distance you can run in the i-th minute.

Comparing Idea 3, we can find that Idea 3 only defines the maximum point and the sum at the zero point of the fatigue function, which are very sparse.

The fourth idea is to define those sums where j is 0 or the i-th minute is running.

(By the way, the sum defined in this way is actually just half of the definition, because the fatigue degree of 1 minute of running is increased by 1, and the fatigue degree of 1 minute of rest is reduced by 1.

The fatigue function is composed of many symmetrical peaks. Although the distance between the convex peaks can be very large, in the dynamic programming algorithm,

Because the optimal solution is required, in fact, the distance between the peaks is not more than 1, otherwise you can run for 1 minute and rest for 1 minute in the middle, forming a small peak)

Of course, this is not an important discovery. What is important is that the double calculation mentioned in the third idea can be solved in the fourth idea!

Code:

#include<iostream>
#include<string.h>
using namespace std;
 
int list[10001];
int sum[10001][501];
int a;
int n, m;
 
int f(int i, int j)
{
	if (i <= 0)return 0;
	if (sum[i][j] >= 0)return sum[i][j];	
	if (j == 0)
	{
		sum[i][j] = f(i - 1, 0);
		for (int k = i-m; k < i; k++)
		{
			a = f(k, i - k);
			if (sum[i][j] < a)sum[i][j] = a;
		}
	}
	else sum[i][j] = f(i - 1, j - 1) + list[i];
	return sum[i][j];
}
 
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> list[i];
	memset(sum, -1, sizeof(sum));
	cout << f(n, 0);
	return 0;
}

The code of idea 4 is almost the same as idea 3. The only difference is that in the f function, when j is not 0, in fact, only a simple statement can calculate sum[i][j]

Nothing else needs to be modified.

If you carefully examine, we can find that both thinking three and thinking four can deal with the loopholes of thinking two-to meet the limitations of the problem, you must rest until the fatigue is 0 before you can continue running.

But the way is quite different.

The third idea is to directly use the summation to push forward to the start of running (the fatigue degree is 0) when calculating the sum at the vertex (j is not 0).

The fourth idea is to push forward directly to the beginning of the rest when seeking the sum where j is 0.

Although the codes of the two ideas are exactly the same when finding the sum where j is 0, because the ideas are different and the ideas are different, the different methods selected to meet the restriction result in different efficiency.

It's almost the hardest DP problem I can AC.

Guess you like

Origin blog.csdn.net/nameofcsdn/article/details/113064148