ICPC训练联盟2021寒假冬令营(8)_2021.01.28_笔记

试题链接

点我进入代码提交OJ

学习笔记 - 动态规划

动态规划方法的编程实验

• 在现实中有一类活动,其过程可以分成若干个互相联系的阶段,在它的每一个阶段都需要作出决策,从而使整个过程达到最好的活动效果。在各个阶段决策依赖于当前状态,也引起状态的转移,而一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义。我们称这种解决多阶段决策问题的方法为动态规划(Dynamic Programming,DP)方法,简称为DP方法,如图所示。
在这里插入图片描述
• DP方法的指导思想是,在每一决策步上,列出各种可能的局部解,然后按某些条件,舍弃那些肯定不能得到最优解的局部解。每一步都经过这样的筛选后,就能大大减少工作量。DP依据的是所谓“最优化原理”,它可以陈述为:“一个最优的决策(判定)序列具有以下性质:不论初始状态和第一步的决策是什么,余下的决策必须相对于前一次决策所产生的新状态构成一个最优决策序列。”换言之,如果有一个决策序列,它包含有非局部最优的决策子序列时,该决策序列一定不是最优的。

线性DP的实验范例

• 初步体验线性DP问题
• 子集和问题
• 最长公共子序列问题

初步体验线性DP问题

在这里插入图片描述
• 阶段k和状态sk:把问题分成n个有顺序且相互联系的阶段,1<=k<=n。例如,上图表示问题被划分成5个阶段。状态sk为第k阶段的某个出发位置。通常一个阶段包含若干状态,状态相对阶段而言。
• 决策uk和允许决策的集合Dk(sk):从第k-1阶段的一个状态演变到第k阶段某状态的选择为决策uk。通常可达至该状态的决策不止一个,这些状态组成了集合Dk(sk)。例如,在图6.1-1中到节点5有两个决策可选择:2→5和3→5,所以D3(5)={2,3}。由始点至终点的一个决策序列简称策略,例如,在图6.1-1中,1→3→5→8→10即为一个策略。
• 状态转移方程和最优化概念:前一阶段的终点就是后一阶段的起点,前一阶段的决策选择导出了后一阶段的状态,这种关系描述了由第k阶段到第k+1阶段状态的演变规律,称为状态转移方程。最优化概念是指经过状态转移方程所确定的运算以后,使全过程的总效益达到最优。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

子集和问题

在这里插入图片描述

最长公共子序列问题

• 给出一个序列,将序列中的一些元素以在原来序列中的次序出现,但这些元素不一定要相邻,这样产生的序列称为原序列的子序列。例如,对于字符串abcdefg,“abc”、“abg”、“bdf”、“aeg” 都是子序列。而对于字符串“HIEROGLYPHOLOGY”和“MICHAELANGELO”,字符串“HELLO” 是公共子序列。

• 最长公共子序列(Longest Commom Subsequence,LCS)问题表述为:给出两个序列,找出两个序列的最长公共子序列。
• 采用DP方法求解最长公共子序列问题,关键是定义阶段、状态和决策。
• 给出两个序列x和y,其长度分别为m和n,x和y的最长公共子序列z计算如下:
• 设序列x=<x1, x2, …, xm>,其第i个前缀为x i ’==<x1, x2, …, xi>,i=0,1,…,m;序列y=<y1, y2, …, yn>,其第i个前缀为y i ’==<y1, y2, …,yi>,i=0,1,…,n;序列z=<z1, z2, …, zk>是x和y的LCS。例如,如果x=<A,B,C,B,D,A,B>,则x4’=<A,B,C,B>,且x0’是空序列。
• 阶段和状态分别是x的前缀指针i和y的前缀指针j,这样可以保证xi-1和yj-1的LCS已经求出。决策是根据LCS的三个性质做最优选择。
• 性质1. 如果xm=yn,则zK=xm=yn,且zk-1’是xm-1’和yn-1’的LCS。
• 性质2. 如果xm≠yn,则zK≠xm,并且z是xm-1’和y的LCS。
• 性质3. 如果xm≠yn,则zK≠yn,并且z是x和yn-1’的LCS。
在这里插入图片描述

最长递增子序列问题

• 设A=<a1, a2, …,an>是由n个不同的实数组成的序列,A的递增子序列L是这样一个子序列L=<aK1, ak2, …, akm>,其中k1<k2<…<km且aK1<ak2<…<akm。最长递增子序列(LongestIncrease Subsequence,LIS)问题就是求A的最长递增子序列,也就是说,求最大的m值。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

0-1背包问题

• 基本的0-1背包
• 完全背包
• 多重背包
• 混合背包
• 分组背包
• 有依赖的背包
• 二维背包

基本的0-1背包问题

在这里插入图片描述
• 用动态规划算法对基本的0-1背包问题进行分析:
• 设B(i, w)表示选择物品{1, 2, …, i }的一个子集且重量限制为w的最优解的值。则对于每个w<=M,B(0, w)=0;对于第i件物品,有放入背包(B(i, w)=B(i-1, w-wi)+pi)和不放入背包(B(i, w)=B(i-1, w))两种选择。所以,B(i, w)计算公式如下:
在这里插入图片描述

求解基本的0-1背包问题的算法

• 输入:n个物品组成的集合,物品i重量为wi,价值为pi;背包的载荷能力M;
• 输出:对于w=0, …, M,在总重量最多为w的条件下,使得n个物品集的子集的价值B[w]达到最大;

for (w=0; w<=M; w++) //初始化
	B[w]=0;
for (i=1; i<=n; i++) //阶段:递推n个物品
	for (w=M; w>=wi; w--) //状态:枚举重量限制
		if (B[w- wi]+ pi> B[w]) //决策:若第i件物品放入背包较优,则调整
			B[w]= B[w- wi] + pi;

完全背包

• 完全背包问题描述如下:
• 给出n种物品和一个载荷能力为M的背包,每种物品都有无限件,物品i重量为wi,价值为pi,其中wi>0,pi>0,1in;求将哪些物品装入背包,可使得使背包里所放物品的总重量不超过M,且背包中物品的价值总和达到最大。
• 完全背包问题和基本的0-1背包问题非常类似,区别就是在完全背包问题中,每种物品有无限件。
• 从每种物品的角度考虑,求解完全背包问题的策略由对某种物品取或者不取变成了取0件、取1件、取2件,……等很多种。按基本的01背包问题求解算法的思路,设f[i][v]表示前i种物品恰放入一个载荷能力为v 的背包的最大价值。则状态转移方程为f[i][v]=max{ f[i-1][v-kw[i]]+kp[i] | 0<=k*w[i]<=v }。

求解完全背包问题的算法
for (i=1; in; i++) //阶段:枚举每个物品
	for (v=0; vM; v++) //状态:枚举背包载荷能力
		for (k=1; kv div w[i]; k++) //决策
			f[i][v]=max{ f[i-1][v], f[i-1][v-k*w[i]]+k*p[i] }

在这里插入图片描述

A - Brackets Sequence (POJ 1141, ZOJ 1463, Ural 1183, UVA 2451)

Let us define a regular brackets sequence in the following way:

  1. Empty sequence is a regular sequence.
  2. If S is a regular sequence, then (S) and [S] are both regular sequences.
  3. If A and B are regular sequences, then AB is a regular sequence.

For example, all of the following sequences of characters are regular brackets sequences:

(), [], (()), ([]), ()[], ()[()]

And all of the following character sequences are not:

(, [, ), )(, ([)], ([(]

Some sequence of characters ‘(’, ‘)’, ‘[’, and ‘]’ is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 … an is called a subsequence of the string b1 b2 … bm, if there exist such indices 1 = i1 < i2 < … < in = m, that aj = bij for all 1 = j = n.
Input
The input file contains at most 100 brackets (characters ‘(’, ‘)’, ‘[’ and ‘]’) that are situated on a single line without any other characters among them.
Output
Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.
Sample Input
([(]
Sample Output
()[()]

中文释义

在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include <iostream>
using namespace std;
const int MAXN = 110;
char str[MAXN];
int dp[MAXN][MAXN],path[MAXN][MAXN];
void output(int i,int j){
    if(i>j) return;
    if(i==j){
        if(str[i]=='[' || str[i]==']') printf("[]");
        else printf("()");
    }
    else if(path[i][j]==-1){
        printf("%c",str[i]);
        output(i+1,j-1);
        printf("%c",str[j]);
    }
    else{
        output(i,path[i][j]);
        output(path[i][j]+1,j);
    }
}
int main(){
    int i,j,k,r,n;
    while(gets(str)){
        n=strlen(str);
        if(n==0){
            printf("\n");
            continue;
        }
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++) dp[i][i]=1;
        for(r=1;r<n;r++)
            for(i=0;i<n-r;i++){
                j=i+r;
                dp[i][j]=INT_MAX;
                if((str[i]=='(' && str[j]==')')||(str[i]=='[' && str[j]==']'))
                    if(dp[i][j]>dp[i+1][j-1])
                        dp[i][j]=dp[i+1][j-1],path[i][j]=-1;
                for(k=i;k<j;k++)
                    if(dp[i][j]>dp[i][k]+dp[k+1][j])
                        dp[i][j]=dp[i][k]+dp[k+1][j],path[i][j]=k;
            }
        output(0,n-1);
        printf("\n");
    }
    return 0;
}

B - Dollars (UVA 147)

在这里插入图片描述

中文释义

在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include<iostream>
#include<cstring>
#include<iomanip>
using namespace std;
const int maxn=30100;
int d[12]={0,10000,5000,2000,1000,500,200,100,50,20,10,5};
long long dp[maxn];
int main()
    {
        int x,y;
        char ch;
        while(cin>>x>>ch>>y)
            {
                int val=x*100+y;
                if(val==0) break;
                memset(dp,0,sizeof(dp));
                dp[0]=1;
                int i,j,k;
                for(i=1;i<=11;i++)
                    {
                        for(j=d[i];j<=val;j++)
                            {
                                dp[j]=dp[j]+dp[j-d[i]];
                            }
                    }
                double dval=val/100.0;
                cout<<setiosflags(ios::right)<<setw(6)<<fixed<<setprecision(2)<<dval;
                cout<<setiosflags(ios::right)<<setw(17)<<dp[val]<<endl;
            }
        return 0;
    }

C - Longest Match (UVA 10100)

在这里插入图片描述

中文释义

在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
char str[20100],st2[20100];
char a[1002][22],b[1002][22];
int f[1002][1002];
int main()
{
    int K=0,tt=0,l,t,t2,l2,ff;
    while(gets(str)!=NULL)
    {
        tt=0;
        t2=0;
        t=0;
        gets(st2);
        ff=0;
        l=strlen(str);
        l2=strlen(st2);
        for(int i=0; i<l; i++)
        {
            if((str[i]>='A'&&str[i]<='Z')||(str[i]>='a'&&str[i]<='z')||(str[i]>='0'&&str[i]<='9'))
            {
                a[tt][t++]=str[i];
                ff=1;
            }
            else
            {
                if(t)
                {
                    a[tt][t]='\0';
                    t=0;
                }
                if((i+1<l)&&((str[i+1]>='a'&&str[i+1]<='z')||(str[i+1]>='A'&&str[i+1]<='Z')||(str[i+1]>='0'&&str[i+1]<='9')))
                {
                    tt++;
                }
            }
        }
        if(((str[l-1]>='A'&&str[l-1]<='Z')||(str[l-1]>='a'&&str[l-1]<='z')||(str[l-1]>='0'&&str[l-1]<='9'))&&t>0)
        {
            a[tt][t]='\0';
        }
        printf("%2d. ",++K);
        if(l==0||l2==0)
        {
            printf("Blank!\n");
            continue;
        }
        if(ff==0)
        {
            printf("Length of longest match: 0\n");
            continue;
        }

        t=0;
        for(int i=0; i<l2; i++)
        {
            if((st2[i]>='A'&&st2[i]<='Z')||(st2[i]>='a'&&st2[i]<='z')||(st2[i]>='0'&&st2[i]<='9'))
            {
                b[t2][t++]=st2[i];
                ff=2;
            }
            else
            {
                if(t)
                {
                    b[t2][t]='\0';
                    t=0;
                }
                if((i+1<l2)&&((st2[i+1]>='0'&&st2[i+1]<='9')||(st2[i+1]>='a'&&st2[i+1]<='z')||(st2[i+1]>='A'&&st2[i+1]<='Z')))
                {
                    t2++;
                }
            }
        }
        if(((st2[l2-1]>='A'&&st2[l2-1]<='Z')||(st2[l2-1]>='a'&&st2[l2-1]<='z')||(st2[l2-1]>='0'&&str[l2-1]<='9'))&&t>0)
        {
            b[t2][t]='\0';
        }
        if(ff!=2)
        {
            printf("Length of longest match: 0\n");
            continue;
        }
        for(int i=0; i<=tt+1; i++)
        {
            f[i][0]=0;
        }
        for(int i=0; i<=t2+1; i++)
        {
            f[0][i]=0;
        }
        for(int i=1; i<=tt+1; i++)
        {
            for(int j=1; j<=t2+1; j++)
            {
                if(strcmp(a[i-1],b[j-1])==0)
                    f[i][j]=f[i-1][j-1]+1;
                else f[i][j]=max(f[i-1][j],f[i][j-1]);
            }
        }
        printf("Length of longest match: %d\n",f[tt+1][t2+1]);
    }
    return 0;
}

D - History Grading (UVA 111)

在这里插入图片描述
在这里插入图片描述

中文释义

在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int order[25], arr[25], d[25][25];
int main()
{
    int n, t;
    scanf("%d",&n);
    // 读入正确的答案顺序
    for(int i=0; i<n; i++)
    {
        scanf("%d",&t);
        order[t-1]=i+1; //放入他排名的位置,所以order就是正确的答案
    }
    while(scanf("%d",&t)!=EOF)
    {
        arr[t-1]=1;
        for(int i=1; i<n; ++i)
        {
            scanf("%d",&t);
            arr[t-1]=i+1;
        }
        // 求出最长公共子序列长度
        memset(d, 0, sizeof(d));
        for(int i=0; i<n; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(order[i]==arr[j])
                    d[i+1][j+1]=max(max(d[i][j]+1,d[i][j+1]),d[i+1][j]);
                else
                    d[i+1][j+1]=max(d[i][j+1],d[i+1][j]);
            }
        }
        printf("%d\n", d[n][n]);
    }
    return 0;
}

E - 滑雪 (POJ 1088)

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5

16 17 18 19 6

15 24 25 20 7

14 23 22 21 8

13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
Sample Output
25

中文释义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int MAXN = 105;
int R,C;
int M[MAXN][MAXN];
int mark_len[MAXN][MAXN];
void init();
void get_map(int R, int C);
int DFS(int x, int y);
int main()
{   
    int max_len = 0;
 
    cin >> R >> C;
    init();
    get_map(R, C);
    for(int i = 0;i < R;i++)
    {
        for (int j = 0; j < C; j++)
        {
            max_len = max(max_len,DFS(i,j));
        }
    }
    cout << max_len << endl;
    system("pause");
    return 0;
}
void init()
{
    memset(M, 0, sizeof(M));
    memset(mark_len,0,sizeof(mark_len));
}
void get_map(int R, int C)
{
    for(int i = 0;i < R;i++)
    {
        for (int j = 0; j < C;j ++)
        {
            cin >> M[i][j];
        } 
    }
}
int DFS(int x, int y)
{
    // 记忆化搜索
    if(mark_len[x][y] != 0)
        return mark_len[x][y];
 
    int len = 1;
    int next[4][2] = {{0, 1},{1, 0},{0,-1},{-1,0}}; // 方向数组
    for (int i = 0; i < 4; i++)
    {
        int nx = x + next[i][0];
        int ny = y + next[i][1];
        if(nx < 0 || ny < 0 || nx == R || ny == C)
            continue;
        else if(M[nx][ny] > M[x][y])
        {
             len = max(len, DFS(nx,ny)+1);
        }
    }
    // 记忆化搜索
    mark_len[x][y] = len;
    return len;
}

F - Charm Bracelet (POJ 3624)

Bessie has gone to the mall’s jewelry store and spies a charm bracelet. Of course, she’d like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a ‘desirability’ factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).

Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.

Input

  • Line 1: Two space-separated integers: N and M
  • Lines 2…N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di

Output

  • Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

Sample Input
4 6
1 4
2 6
3 12
2 7
Sample Output
23

中文释义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述

解题代码

#include<iostream>
#include<fstream>
using namespace std;
static const int N = 3403;
static const int M = 12881;
static int f[M];
static int c[N], v[N];
int main()
{
	int n, m;
	while (cin >> n >> m)
	{
		int i, j;
		for (i = 1; i <= n; i++)
		{
			cin >> c[i] >> v[i];
		}
        memset(f, 0, sizeof f);
		for (i = 1; i <= n; i++)
		{
			for(j = m; j >= c[i]; j--) /* j's domain */
			{
				if (f[j] < f[j - c[i]] + v[i])
					f[j] = f[j - c[i]] + v[i];
			}
		}
		cout << f[m] << "\n";
	}
 
	return 0;
}

G - Dollar Dayz (POJ 3181)

Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for $1, $2, and $3. Farmer John has exactly $5 to spend. He can buy 5 tools at $1 each or 1 tool at $3 and an additional 1 tool at $2. Of course, there are other combinations for a total of 5 different ways FJ can spend all his money on tools. Here they are:

    1 @ US$3 + 1 @ US$2

    1 @ US$3 + 2 @ US$1

    1 @ US$2 + 3 @ US$1

    2 @ US$2 + 1 @ US$1

    5 @ US$1

Write a program than will compute the number of ways FJ can spend N dollars (1 <= N <= 1000) at The Cow Store for tools on sale with a cost of 1.. 1.. 1..K (1 <= K <= 100).
Input
A single line with two space-separated integers: N and K.
Output
A single line with a single integer that is the number of unique ways FJ can spend his money.
Sample Input
5 3
Sample Output
5

中文释义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题分析

在这里插入图片描述
在这里插入图片描述

解题代码

#include<iostream>
using namespace std;
int n, k;
long long MOD = 1;
long long dp1[1005], dp2[1005];
int main()
{
	scanf("%d%d", &n, &k);
	for (int i = 0; i < 18; i++)
		MOD *= 10;
	dp2[0] = 1;
	for (int i = 1; i <= k; i++)
		for (int j = 0; j <= n; j++)
		{
			if (j - i >= 0)
			{
				dp1[j] = dp1[j] + dp1[j - i] + (dp2[j] + dp2[j - i]) / MOD;  //算出i,j对应的高位
				dp2[j] = (dp2[j] + dp2[j - i]) % MOD;           //算出低位
			}
			else
			{
				dp1[j] = dp1[j];
				dp2[j] = dp2[j];
			}
		}
	if (dp1[n]) printf("%lld", dp1[n]);
	printf("%lld\n", dp2[n]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值