东大acm预备队训练第七周(动态规划)

1 P1048 [NOIP2005 普及组] 采药

[NOIP2005 普及组] 采药

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 2 2 2 个整数 T T T 1 ≤ T ≤ 1000 1 \le T \le 1000 1T1000)和 M M M 1 ≤ M ≤ 100 1 \le M \le 100 1M100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。

接下来的 M M M 行每行包括两个在 1 1 1 100 100 100 之间(包括 1 1 1
100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

样例 #1
样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

3

提示

【数据范围】

  • 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M10
  • 对于全部的数据, M ≤ 100 M \le 100 M100

【题目来源】

NOIP 2005 普及组第三题

题目解析

简单的背包问题。

代码

#include <bits/stdc++.h>
using namespace std;

int T,M;
struct med
{
	int time,w;
}m[105];
int dp[105][1005];

int main()
{
	cin>>T>>M;
	for(int i=1;i<=M;++i)
		scanf("%d %d",&m[i].time,&m[i].w);
	for(int i=1;i<=M;++i)  //i代表草药个数 
	{
		for(int j=0;j<=T;++j)  //j代表所花费时间 
		{
			dp[i][j]=dp[i-1][j];
			if(j>=m[i].time)
				dp[i][j]=max(dp[i][j],dp[i-1][j-m[i].time]+m[i].w);
		}
	}
	cout<<dp[M][T];
	return 0;
}

2 B3637 最长上升子序列

最长上升子序列

题目描述

这是一个简单的动规板子题。

给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。

最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。

输入格式

第一行,一个整数 n n n,表示序列长度。

第二行有 n n n 个整数,表示这个序列。

输出格式

一个整数表示答案。

样例 #1
样例输入 #1

6
1 2 4 1 3 4

样例输出 #1

4

提示

分别取出 1 1 1 2 2 2 3 3 3 4 4 4 即可。

题目解析

动态规划问题。状态量f[i]为以第i个数字结尾的最长上升子序列长度。

代码

#include <bits/stdc++.h>
using namespace std;

int n;
int a[5005];
int f[5005];

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)
        f[i]=1;
    for(int i=2;i<=n;++i)
    {
        for(int j=1;j<i;++j)
        {
            if(a[j]<a[i])
            {
                if(f[i]<f[j]+1)
                    f[i]=f[j]+1;
            }
        }
    }

    int ans=1;
    for(int i=1;i<=n;++i)
    {
        if(ans<f[i])
            ans=f[i];
    }
    cout<<ans;

    return 0;
}

3 P1115 最大子段和

最大子段和

题目描述

给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n n n

第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai

输出格式

输出一行一个整数表示答案。

样例 #1
样例输入 #1

7
2 -4 3 -1 2 -4 3

样例输出 #1

4

提示
样例 1 解释

选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,1,2},其和为 4 4 4

数据规模与约定
  • 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 1 0 3 n \leq 2 \times 10^3 n2×103
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 − 1 0 4 ≤ a i ≤ 1 0 4 -10^4 \leq a_i \leq 10^4 104ai104

题目解析

动态规划问题。求最大子段和,可以先以第一个数字为一个子段,如果加上后一个数和增大,则加上这个数更新子段,反之,则舍去之前的子段,以这个数为一个新的子段。重复这个过程最后找到最大子段和。

代码

#include <bits/stdc++.h>
using namespace std;

int n;
int a[200005];

int main()
{
    cin>>n;
    int ans=0;
    int tmp=0;
    for(int i=1;i<=n;++i)
    {
        cin>>a[i];
        if(i==1)
        {
            ans=a[i];
            tmp=a[i];
        }
        else
            tmp=max(a[i],tmp+a[i]);
        ans=max(ans,tmp);
    }
    cout<<ans<<endl;
    return 0;
}

4 LCS

LCS

题面翻译

题目描述:

给定一个字符串 s s s 和一个字符串 t t t ,输出 s s s t t t 的最长公共子序列。

输入格式:

两行,第一行输入 s s s ,第二行输入 t t t

输出格式:

输出 s s s t t t 的最长公共子序列。如果有多种答案,输出任何一个都可以。

说明/提示:

数据保证 s s s t t t 仅含英文小写字母,并且 s s s t t t 的长度小于等于3000。

题目描述

文字列 s s s および t t t が与えられます。 s s s の部分列かつ t t t
の部分列であるような文字列のうち、最長のものをひとつ求めてください。

输入格式

入力は以下の形式で標準入力から与えられる。

s s s t t t

输出格式

s s s の部分列かつ t t t の部分列であるような文字列のうち、最長のものをひとつ出力せよ。
答えが複数ある場合、どれを出力してもよい。

样例 #1
样例输入 #1

axyb
abyxb

样例输出 #1

axb

样例 #2
样例输入 #2

aa
xayaz

样例输出 #2

aa

样例 #3
样例输入 #3

a
z

样例输出 #3

样例 #4
样例输入 #4

abracadabra
avadakedavra

样例输出 #4

aaadara

提示
注釈

文字列 x x x部分列とは、 x x x から 0 0 0
個以上の文字を取り除いた後、残りの文字を元の順序で連結して得られる文字列のことです。

制約
  • s s s および t t t は英小文字からなる文字列である。
  • 1   ≤   ∣ s ∣ ,   ∣ t ∣   ≤   3000 1\ \leq\ |s|,\ |t|\ \leq\ 3000 1  s, t  3000
Sample Explanation 1

答えは axb または ayb です。 どちらを出力しても正解となります。

Sample Explanation 3

答えは `` (空文字列) です。

题目解析

关于最长公共子序列的问题。

代码

#include <iostream>
#include <cstring>
using namespace std;

int f[3000][3000];
char a[3005],b[3005],ans[3001];

int main()
{
	scanf("%s%s", a + 1, b + 1);
	for(int i=1;i<=strlen(a+1);i++)
	{
		for(int j=1;j<=strlen(b+1);j++)
		{
			if(a[i]==b[j])
			{
				if(i>0&&j>0)
					f[i][j]=max(f[i][j],f[i-1][j-1]+1);
				else
					f[i][j]=max(f[i][j],1);
			}
			if(i>0)
				f[i][j]=max(f[i][j],f[i-1][j]);
			if(j>0)
				f[i][j]=max(f[i][j],f[i][j-1]);		
		}
	}
	int x=strlen(a+1);
    int y=strlen(b+1);
    while(f[x][y])
    {
        if(a[x]==b[y])
        {
            ans[f[x][y]]=a[x];
            x--;
            y--;
        }
        else
        {
            if(f[x][y]==f[x-1][y])
                x--;
            else
                y--;
        }
    }
    printf("%s", ans + 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷神星ceres

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值