秋季学期一起开心讲课-week05-dp

1.初步动态规划:

①:数字三角形问题:

   有形如下图所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一起走到底层,要求找出一条路径,使路径上的值最大。

样例输入:

5

13

11 8

12 7 26

6 14 15 8

12 7 13 24 11

样例输出:

86(13->8->26->15->24)
分析:因为从上面往下走,必须要知道左右两边那条路更大。所以要从底层开始层层递进,直至找到所标记的点。

dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 10000
int a[maxn][maxn];
int dp[maxn][maxn];
using namespace std;
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i ++)
		for(int j = 0; j <= i; j++)
			scanf("%d",&a[i][j]);
	for(int i = 0; i < n; i ++)
		dp[n-1][i] = a[n-1][i];
	for(int i = n-2; i >= 0; i--)
		for(int j = 0;j <= i; j ++)
			dp[i][j] = max(dp[i+1][j],dp[i+1][j+1]) + a[i][j];
	printf("%d\n",dp[0][0]);
	return 0;
}

2.序列dp

1)最长上升子序列LIS

        输入n及一个长度为n的数列,求出此序列的最长上升子序列长度。上升子序列指的是对于任意的i<j都满足ai<aj的子序列。(1<=n<=1000,0<=ai<=1000000)

样例输入:

5

4 2 3 1 5

样例输出:

3(最长上升子序列为2, 3, 5)

#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 1005
int a[maxn];
int dp[maxn];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 1; i <= n; i ++)
	{
		scanf("%d",&a[i]);
		dp[i] = 1;
	}
	int ans = 1;
	for(int i = 1; i <= n; i ++)
	{
		for(int j = 1; j < i; j ++)
		{
			if(a[j]<a[i])
				dp[i] = max(dp[i],dp[j]+1);
		}
		ans = max(ans,dp[i]);
	}
	printf("%d\n",ans);
	return 0;
}

3.字符串的最长公共子序列和最大公共子串

区别:

例如:acdea

           acddda

最长公共子序列为4(“acda”)

最大公共子串为3(“acd”)

1>.最长公共子序列

动态方程:dp[i+1][j+1] = dp[i][j] + 1;

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
int dp[maxn][maxn];
int main()
{
	char s1[maxn],s2[maxn];
	while(~scanf("%s",s1))
	{
		scanf("%s",s2);
		int len1 = strlen(s1);
		int len2 = strlen(s2);
		memset(dp,0,sizeof(dp));
		for(int i = 0; i < len1; i ++)
			for(int j = 0; j < len2; j ++)
			{
				if(s1[i]==s2[j])
					dp[i+1][j+1] = dp[i][j] + 1;
				else
					dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]); 
			}
		printf("%d\n",dp[len1][len2]);
	}
}

2>最大公共子序列

在情况1的基础上稍加改动

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
int dp[maxn][maxn];
int main()
{
	char s1[maxn],s2[maxn];
	while(~scanf("%s",s1))
	{
		scanf("%s",s2);
		int len1 = strlen(s1);
		int len2 = strlen(s2);
		memset(dp,0,sizeof(dp));
		int ans = 0;
		for(int i = 0; i < len1; i ++)
			for(int j = 0; j < len2; j ++)
			{
				if(s1[i]==s2[j])
					dp[i+1][j+1] = dp[i][j] + 1;
				else
					dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]); 
				ans = max(ans,dp[i+1][j+1]);
			}
		printf("%d\n",dp[len1][len2]);
	}
}

2.背包问题:

1>01背包 有n件物品,背包总量为m,给出n种物品的价格和重量,求出最优解。

#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 5001
using namespace std;
int a[maxn],b[maxn];
int dp[maxn];
int main() 
{
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i = 1; i <= n ; i ++)
		scanf("%d %d",&a[i],&b[i]);//价值为a,重量为b 
	for(int i = 1; i <= n; i ++)
		for(int j = m; j >= b[i] ; j--)
		{
			dp[j] = max(dp[j],dp[j-b[i]]+a[i]);
		}
	printf("%d\n",dp[m]);
		
}

完全背包

有N种物品和容量为M的包,其中N种物品有无数件,并给出N种物品的价值和重量。

#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 5001
using namespace std;
int a[maxn],b[maxn];
int dp[maxn];
#define INF 0x3f3f3f3f
int main() 
{
	int n,m;
	scanf("%d %d",&n,&m);
	memset(dp,INF,sizeof(dp)); 
	for(int i = 1; i <= n ; i ++)
		scanf("%d %d",&a[i],&b[i]);//价值为a,重量为b 
	for(int i = 1; i <= n; i ++)
		for(int j = b[i]; j <= m; j ++)
		{
			dp[j] = max(dp[j],dp[j-b[i]]+a[i]);
		}
		if(dp[m]==INF)
				printf("NO\n");
		else
			printf("%d\n",dp[m]);
		
}

多重背包

有一个容量为V的背包和N件物品,第i件物品最多有Num[i]件,每件物品的重量是weight[i],收益是cost[i]。

#include <iostream>
using namespace std;
 const int N = 3;//物品个数
const int V = 8;//背包容量
int Weight[N + 1] = {0,1,2,2};
int Value[N + 1] = {0,6,10,20};
int Num[N + 1] = {0,10,5,2};
 int f[V + 1] = {0};
void ZeroOnePack(int nWeight,int nValue)
{
	for (int v = V;v >= nWeight;v--)
	{
		f[v] = max(f[v],f[v - nWeight] + nValue);
	}
}
void CompletePack(int nWeight,int nValue)
{
	for (int v = nWeight;v <= V;v++)
	{
		f[v] = max(f[v],f[v - nWeight] + nValue);
	}
}
 
int MultiKnapsack()
{
	int k = 1;
	int nCount = 0;
	for (int i = 1;i <= N;i++)
	{
		if (Weight[i] * Num[i] >= V)
		{
			//完全背包:该类物品原则上是无限供应,
			//此时满足条件Weight[i] * Num[i] >= V时,
			//表示无限量供应,直到背包放不下为止.
			CompletePack(Weight[i],Value[i]);
		}
		else
		{
			k = 1;
			nCount = Num[i];
			while(k <= nCount)
			{
				ZeroOnePack(k * Weight[i],k * Value[i]);
				nCount -= k;
				k *= 2;
			}
			ZeroOnePack(nCount * Weight[i],nCount * Value[i]);
		}
	}
	return f[V];
}
 
int main()
{
	cout<<MultiKnapsack()<<endl;
	return 0
}

猜你喜欢

转载自blog.csdn.net/qiulianshaonv_wjm/article/details/83754232