2月9号自习总结

目录

动态规划刷题 :

摆花:[NOIP2012 普及组] 摆花 - 洛谷

编辑距离:编辑距离 - 洛谷

5倍经验日:5 倍经验日 - 洛谷

疯狂的采药:疯狂的采药 - 洛谷


动态规划刷题 :

摆花:[NOIP2012 普及组] 摆花 - 洛谷

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 mm 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 nn 种花,从 11 到 nn 标号。为了在门口展出更多种花,规定第 ii 种花不能超过 aiai​ 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

输入格式

第一行包含两个正整数 nn 和 mm,中间用一个空格隔开。

第二行有 nn 个整数,每两个整数之间用一个空格隔开,依次表示 a1,a2,⋯ ,ana1​,a2​,⋯,an​。

输出格式

一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 106+7106+7 取模的结果。

输入输出样例

输入 #1

2 4
3 2

输出 #1

2

说明/提示

【数据范围】

对于 20%20% 数据,有 0<n≤8,0<m≤8,0≤ai≤80<n≤8,0<m≤8,0≤ai​≤8。

对于 50%50% 数据,有 0<n≤20,0<m≤20,0≤ai≤200<n≤20,0<m≤20,0≤ai​≤20。

对于 100%100% 数据,有 0<n≤100,0<m≤100,0≤ai≤1000<n≤100,0<m≤100,0≤ai​≤100。

NOIP 2012 普及组 第三题

 思路:

动归解题:

1.确定dp数组(dp table)以及下标的含义:dp[i][j]表示第i个花后总共摆了j盆的方案数

2.确定递推公式:

    dp[i][p + l] += dp[i - 1][p];//将遍历到i-1花摆p盆的总方案数加到i花摆p+l盆的方案数上

3.根据状态转移方程,进行状态更新需要3层循环,第一层循环花的种类数,第二层循环用     来列举第i种花摆多少盆,第三层循环选择到了第i种花时总共摆了多少盆

 代码:

#include<stdio.h>
const int mo = 1e6 + 7;
int main() {
	int n, m, arr[101], dp[101][101] = {0}; //dp[i][j]表示第i个花后总共摆了j盆的方案数
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &arr[i]);
	}
	dp[0][0] = 1;
	for (int i = 1; i <= n; i++) {//遍历到第i种花
		for (int l = 0; l <= arr[i]; l++) {//第i种花摆l盆,最多摆arr[i]盆
			for (int p = 0; p <= m - l; p++) { //已经装p盆
				dp[i][p + l] += dp[i - 1][p];//将遍历到i-1花摆p盆的总方案数加到i花摆p+l盆的方案数上
				dp[i][p + l] %= mo;
			}
		}
	}
	for(int i=1;i<=4;i++){
		for(int ij=1;ij<=4;ij++){
			printf("%d ",dp[i][ij]);
		}
		printf("\n");
	}
	printf("%d", dp[n][m] % mo);
}

编辑距离:编辑距离 - 洛谷

题目描述

设 AA 和 BB 是两个字符串。我们要用最少的字符操作次数,将字符串 AA 转换为字符串 BB。这里所说的字符操作共有三种:

  1. 删除一个字符;

  2. 插入一个字符;

  3. 将一个字符改为另一个字符。

A,BA,B 均只包含小写字母。

输入格式

第一行为字符串 AA;第二行为字符串 BB;字符串 A,BA,B 的长度均小于 20002000。

输出格式

只有一个正整数,为最少字符操作次数。

输入输出样例

输入 #1

sfdqxbw
gfdgw

输出 #1

4

说明/提示

对于 100%100% 的数据,1≤∣A∣,∣B∣≤20001≤∣A∣,∣B∣≤2000。

 思路(动态规划):

dp数组(dp table)以及下标的含义:

     dp[i][j]表示a长度为i的子串和b长度为j的子串的最少操作次数

递推公式:

if (a[i] == b[p]) {//当两个字符相等时
	dp[i][p] = dp[i - 1][p - 1] ;//不用编辑,继承上个状态
} else {
	dp[i][p] = 1 + std::min(std::min(dp[i - 1][p], dp[i][p - 1]), dp[i - 1][p - 1]); //分别为删除,插入,改变
}

初始化:

 

 代码:

#include<bits/stdc++.h>
char a[2001], b[2001];
int dp[2001][2001] = {0};//dp[i][j]表示a长度为i的子串和b长度为j的子串的最少操作次数
int main() {
	gets(a + 1);
	gets(b + 1);
	for (int i = 1; i <= strlen(a + 1); i++) {
		dp[i][0] = i;//a长为i,b长为0,则操作i次
		for (int p = 1; p <= strlen(b + 1); p++) {
			dp[0][p] = p;//b长为i,a长为0,则操作i次
			if (a[i] == b[p]) {
				dp[i][p] = dp[i - 1][p - 1] ;//不用改
			} else {
				dp[i][p] = 1 + std::min(std::min(dp[i - 1][p], dp[i][p - 1]), dp[i - 1][p - 1]); //分别为删除,插入,改变
			}
		}
	}
	printf("%d", dp[strlen(a + 1)][strlen(b + 1)]);
}

5倍经验日:5 倍经验日 - 洛谷

题目背景

现在乐斗有活动了!每打一个人可以获得 5 倍经验!absi2011 却无奈的看着那一些比他等级高的好友,想着能否把他们干掉。干掉能拿不少经验的。

题目描述

现在 absi2011 拿出了 xx 个迷你装药物(嗑药打人可耻…),准备开始与那些人打了。

由于迷你装药物每个只能用一次,所以 absi2011 要谨慎的使用这些药。悲剧的是,用药量没达到最少打败该人所需的属性药药量,则打这个人必输。例如他用 22 个药去打别人,别人却表明 33 个药才能打过,那么相当于你输了并且这两个属性药浪费了。

现在有 nn 个好友,给定失败时可获得的经验、胜利时可获得的经验,打败他至少需要的药量。

要求求出最大经验 ss,输出 5s5s。

输入格式

第一行两个数,nn 和 xx。

后面 nn 行每行三个数,分别表示失败时获得的经验 loseilosei​,胜利时获得的经验 winiwini​ 和打过要至少使用的药数量 useiusei​。

输出格式

一个整数,最多获得的经验的五倍。

输入输出样例

输入 #1

6 8
21 52 1
21 70 5
21 48 2
14 38 3
14 36 1
14 36 2

输出 #1

1060

说明/提示

【Hint】

五倍经验活动的时候,absi2011 总是吃体力药水而不是这种属性药。

【数据范围】

  • 对于 10%10% 的数据,保证 x=0x=0。

  • 对于 30%30% 的数据,保证 0≤n≤100≤n≤10,0≤x≤200≤x≤20。

  • 对于 60%60% 的数据,保证 0≤n,x≤1000≤n,x≤100, 10<losei,wini≤10010<losei​,wini​≤100,0≤usei≤50≤usei​≤5。

  • 对于 100%100% 的数据,保证 0≤n,x≤1030≤n,x≤103,0<losei≤wini≤1060<losei​≤wini​≤106,0≤usei≤1030≤usei​≤103。

【题目来源】

fight.pet.qq.com

absi2011 授权题目

 思路:

一个药只能用一次,每次用完去打人之后无论输赢这个药的药效都会消失,那么,将这个问题看做一个01背包问题药的总量为背包容量,打人用多少药相当于消耗多少的背包容量,

当打某个人时的剩余药量小于这个人的药量时必输,即这个物体背包装不下这个物体,在使用药的有限个数内要使得获得的经验最多。

用一维dp,dp[i]表示用i瓶药获得的最多的经验

 代码:

#include<stdio.h>
#include<iostream>
using namespace std;
const int ma=1001;
struct pp {
	int lose;
	int win;
	int use;
} mm[ma];
int dp[ma*ma];//表示用i瓶药获得的最多的经验
int main() {
	int n, x;
	scanf("%d %d", &n, &x);
	for (int i = 1; i <=n; i++) {
		scanf("%d %d %d", &mm[i].lose, &mm[i].win, &mm[i].use);
	}
	for(int i=1;i<=n;i++){//决策前i个人
		for(int k=x;k>=mm[i].use;k--){//可以战胜,普通0/1背包,正序遍历
			dp[k]=max(dp[k]+mm[i].lose,dp[k-mm[i].use]+mm[i].win);//选择输或赢
		}
		for(int k=mm[i].use-1;k>=0;k--){//所拥有的药的总数小于这个人的药,必输(用药小于k的)
			dp[k]+=mm[i].lose;
		}
	}
	printf("%lld",5ll*dp[x]);
}

疯狂的采药:疯狂的采药 - 洛谷

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

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

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

此题和原题的不同点:

11. 每种草药可以无限制地疯狂采摘。

22. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 tt 和代表山洞里的草药的数目 mm。

第 22 到第 (m+1)(m+1) 行,每行两个整数,第 (i+1)(i+1) 行的整数 ai,biai​,bi​ 分别表示采摘第 ii 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

输入输出样例

输入 #1

70 3
71 100
69 1
1 2

输出 #1

140

说明/提示

数据规模与约定

  • 对于 30%30% 的数据,保证 m≤103m≤103 。

  • 对于 100%100% 的数据,保证 1≤m≤1041≤m≤104,1≤t≤1071≤t≤107,且 1≤m×t≤1071≤m×t≤107,1≤ai,bi≤1041≤ai​,bi​≤104。

 思路:

该题属于完全背包问题,与01背包不同的是每一种物体不在是只有一个而是有很多个

 代码:

#include<stdio.h>
#include<iostream>
using namespace std;
long long t, m;
struct yao {
	int time;
	int value;
} all[11000];
long long dp[10000007]={0};//在时间i的限制下所能获得的最大总价值为dp[i]
int main() {
	scanf("%lld %lld", &t, &m);
	for(int i=1;i<m;i++){
		scanf("%lld %lld",&all[i].time,&all[i].value);
	}
	for(int i=1;i<m;i++){
		for(int ik=all[i].time;ik<=t;ik++){//第i个物体的时间,完全背包,正序,ik++
		//逆序为for(int ik=t;ik>=all[i].time;ik--){//若每个物体只有一个
			dp[ik]=max(dp[ik],dp[ik-all[i].time]+all[i].value);
			//ik时的状态等于  更新之前ik的状态  和  还没有装下物体i时装下的最大价值加上物体i的价值  之间的最大值
		}
	}
	printf("%lld",dp[t]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值