算法设计与分析--动态规划

动态规划:

动态规划:通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划问题常常适用于有重叠子问题和最优子结构性质的问题。动态规划视图仅仅解决每个子问题一次,从而减少计算量,一旦某个给定的子问题已经解出,则将其记忆化存储,以便下一次需要同一个子问题时直接查表。

与分治法的区别:

分治法:将分解后的子问题看作是相互独立的,通过递归来做

动态规划:将分解后的子问题理解为相互之间有联系,有重叠部分,需要记忆,通常用迭代来做。在用递归进行自顶向下解问题时,每次产生的子问题并不是新问题,有些子问题被反复计算多次,所以动态规划每个子问题只解一次,供以后使用。

 

最长公共子序列问题

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description

给定两个序列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

Input

输入数据有多组,每组有两行 ,每行为一个长度不超过500的字符串(输入全是大写英文字母(A,Z)),表示序列X和Y。

Output

每组输出一行,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出0。

Sample Input

ABCBDAB
BDCABA

Sample Output

4

这个题(还有很多这个题)困了我很长时间,首先是之前学习的时候没有用心,很多东西学了就忘了,还有就是上课没有认真听讲,做题的时候没有自己思考的过程,以后一定要改正(背上插满flag);

当前字符相等的时候,结果应该是前面的一个串+1,不相等的时候应该取前面字符串长度的最大值。

#include 

using namespace std;

// 动态规划 最长子序列
int dp[1001][1001] = {0};

int main()
{
    string a, b;
    while(cin>>a>>b){
    for(int i = 1; i <= a.length(); i++){
        for(int j = 1; j <= b.length(); j++){
            if(a[i - 1] == b[j - 1])  // 由于是递归叠加,所以对前一个字符进行比较,最终输出的的是dp[lenx][leny]
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    cout<

初始化dp,可以使用memset(dp, 0, sizeof(dp);

其他方式可能会runtime error

 

高数Umaru系列(9)——哈士奇

01背包问题:n件物品,容量为v,第i件物品的费用是c[i], 价值是 w[i],不超过总重量且价值和最大:

状态转移方程: f [i] [v]  = max {   f [i-1] [v],  f [i-1] [v - c[i] ]   +   w[i] }

解释:前i件物品恰好放入背包内可以获得的最大价值f[i][v],若只考虑第i件物品的策略,放或者不放,那么就转化为只牵扯前 i-1件物品的问题。如果放入i,问题就转为  “前 i-1 件物品放入剩下容量为 v- c[i] 的背包中”,此时获得的最大价值就是 f[i-1][v-c[i]]在加上通过放入第i件物品获得的价值 w[i] 

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description

由于高数巨养的喵星人太傲娇了,要天天吃新鲜猫粮而且还经常欺负高数巨,所以高数巨决定买几条哈士奇尝尝鲜。这天高数巨来到了二手狗市场买哈士奇,高数巨看完了所有的哈士奇,记下了每条哈士奇的价格,并根据对它们的好感程度给它们每只都赋予了一个萌值。高数现在手里有X元,她想通过购买若干条哈士奇来获得尽可能多的萌值。现在给定高数巨手里的钱X以及N条哈士奇的价格和萌值,求高数巨最多可获得多少萌值

Input

 多组输入。

对于每组输入,第一行有两个整数N,X(1 < = N < = 100,1 < = X < = 1000),分别表示哈士奇的数量和高数巨的钱数

接下来的N行每行有两个整数Pi,Mi(1 < = Pi,Mi < = 100),分别表示第i条哈士奇的价格和萌值

Output

对于每组数据,输出一个整数,表示高数巨最多可以获得的萌值,每组输出占一行

Sample Input

2 100
50 20
60 40
3 100
20 55
20 35
90 95
1 10
20 50

Sample Output

40
95
0
#include 
using namespace std;

int main()
{
    int n, x;
    int pi[105], mi[105];
    while(~scanf("%d %d", &n, &x))
    {
        int dp[1005] = {0}; // 注意每次都初始化置位0
        for(int i = 0; i < n; i++)
            cin>>pi[i]>>mi[i];

        for(int i = 0; i < n; i++)
        {
            for(int j = x; j >= pi[i]; j--)  // 从总钱数开始
            {
                dp[j] = max(dp[j], dp[j - pi[i]] + mi[i]);
                // 公式是:不买 和 买当前的得到的萌值
            }
        }
        printf("%d\n", dp[x]);
    }
    return 0;
}

 

最少硬币问题

动态迁移方程为   dp[k] = min{dp[k-t[i]]+1,dp[k]}   这个是最小值min

就是,将第i个硬币拿出去得到的一个最少的找硬币数+1,和原硬币数相比最小的那个就是结果  

 

Problem Description

设有n种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。
对任意钱数0≤m≤20001,设计一个用最少硬币找钱m的方法。
对于给定的1≤n≤10,硬币面值数组T和可以使用的各种面值的硬币个数数组Coins,以及钱数m,0≤m≤20001,计算找钱m的最少硬币数。

Input

输入数据第一行中只有1个整数给出n的值,第2行起每行2个数,分别是T[j]和Coins[j]。最后1行是要找的钱数m。

Output

输出数据只有一个整数,表示计算出的最少硬币数。问题无解时输出-1。

Sample Input

3
1 3
2 3
5 3
18

Sample Output

5

Hint

Source

#include 

using namespace std;

int main()
{
    int n, m;
    cin>>n;
    int T[20], coin[20];
    for(int i = 1; i <= n; i++)
    {
        cin>>T[i]>>coin[i];
    }
    cin>>m;
    int dp[20002];
    for(int i = 1; i <= m; i++)  // 注意这里不可以对dp[0]进行置数操作, 因为是递归,所以i从1开始
    {
        dp[i] = 9999;  // 置数为9999设为最大值
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= coin[i]; j++)
        {
            for(int k = m; k >= T[i]; k--)
            {
                dp[k] = min(dp[k-T[i]] + 1, dp[k]);
            }
        }
    }
    if(dp[m] == 9999)
        cout<<-1<

 

数字三角形问题

Time Limit: 1000 ms Memory Limit: 65536 KiB

Submit Statistic

Problem Description

给定一个由n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
  
对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底的路径经过的数字和的最大值。

Input

输入数据的第1行是数字三角形的行数n,1≤n≤100。接下来n行是数字三角形各行中的数字。所有数字在0..99之间。

Output

输出数据只有一个整数,表示计算出的最大值。

Sample Input

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

Sample Output

30

自底向上寻找最大

#include 

using namespace std;

int a[101][101], n;

void findMax()
{
    for(int i = n - 2; i >= 0; i--)
    {
        for(int j = 0; j <= i; j++)
        {
            a[i][j] = max(a[i + 1][j], a[i + 1][j + 1]) + a[i][j];
        }
    }
    printf("%d\n", a[0][0]);
}

int main()
{
    cin>>n;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            cin>>a[i][j];
        }
    }
    if(n == 1)
        printf("%d\n", a[0][0]);
    else
        findMax();
    return 0;
}

 

石子合并问题

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description

在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
对于给定n堆石子,计算合并成一堆的最小得分和最大得分。

Input

输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。

Output

输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。

Sample Input

4
4 4 5 9

Sample Output

43
54

石子合并这个题和贪心里面那个最差最优合并数有点像?但是不同需求算法不同

和合并序列最大最小那个问题不一样的是只能按照给定顺序和相邻的石子堆进行合并,并且是一个环,也就是最后一个可以和第一个进行合并

解法:将直线连着铺两次在一条直线上,就转化成了直线版了。算出从1到2n的dp值,再比较出所有区间长度为n的dp的最大或最小值。区间长度为n的一共为n组。

#include
#define INF 0x3f3f3f3f
using namespace std;
int dp1[204][204];//最小
int dp2[204][204];//最大
int n;
void init()
{
	for(int i=0;i<=n*2;i++)
		for(int j=0;j<=n*2;j++)
		{
			if(i==j)
			{
				dp1[i][j]=0;
				dp2[i][j]=0;
			}
			else{
				dp1[i][j]=INF;
				dp2[i][j]=0;
			}
		}
}
int max(int a,int b)
{
	return a>b?a:b;
}
int min(int a,int b)
{
	return a>n)
	{
//		init();
		int i,j,k,len;
		int num[204]={0};//前i项和
		int a[204];
		a[0]=0;
		for(i=1;i<=n;i++)
		{
			cin>>a[i];
			num[i]=num[i-1]+a[i];
		}
		for(j=1;j<=n;j++)
		{
			num[i]=num[i-1]+a[j];
			i++;
		}
		for(len=2;len<=n*2;len++)//区间长度要变
			for(i=1;idp2[i][i+n-1]?MAX:dp2[i][i+n-1];
			MIN=MIN

 

 

 

 

 

你可能感兴趣的:(算法+剑指offer,动态规划)