dp(寒假集训第一天)

今天学习动态规划

有两个经典例题:

1,最长公共子序列问题

2,最长不下降序列问题

然后就是题单:

1,洛谷  p1192[台阶问题]

# 台阶问题

## 题目描述

有 N级台阶,你一开始在底部,每次可以向上迈  k级台阶,问到达第 N级台阶有多少种不同方式。

## 输入格式

两个正整数 $N,K$。

## 输出格式

一个正整数 ans\pmod{100003},为到达第 $N$ 级台阶的不同方式数。

## 样例 #1

### 样例输入 #1

```
5 2
```

### 样例输出 #1


8

提示

- 对于 20% 的数据,$1\leq N\leq10$,$1\leq K\leq3$;
- 对于 40% 的数据,$1\leq N\leq1000$;
- 对于 100% 的数据,$1\leq N\leq100000$,$1\leq K\leq100$。

这道题先试着写一下案例,然后联想dp的思想,就会发现大概是一个斐波那契的算法。

注意令a【0】=1;

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int main() {
	int n, k;
	scanf("%d %d", &n, &k);
	long long a[100005] = { 0 };
	a[0] = 1;
	a[1] = 1;
	for (int i = 2; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if(i - j >= 0) {
				a[i] += a[i - j];
				a[i]=a[i] % 100003; 
 //注意这里数据比较大,必须时刻模100003;不然最后输出会变成0;
			}
		}
	}
	printf("%lld\n", a[n]%100003);
}

2,洛谷 p1216【数字三角形】

# [USACO1.5] [IOI1994]数字三角形 Number Triangles

## 题目描述

观察下面的数字金字塔。


写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

![](https://cdn.luogu.com.cn/upload/image_hosting/95pzs0ne.png)

在上面的样例中,从 $7 \to 3 \to 8 \to 7 \to 5$ 的路径产生了最大权值。

## 输入格式

第一个行一个正整数 $r$ ,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

## 输出格式

单独的一行,包含那个可能得到的最大的和。

## 样例 #1

### 样例输入 #1

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

### 样例输出 #1

```
30
```

## 提示

【数据范围】  
对于 $100\%$ 的数据,$1\le r \le 1000$,所有输入在 $[0,100]$ 范围内。

题目翻译来自NOCOW。

USACO Training Section 1.5

IOI1994 Day1T1

比较简单的一道题;

注意2维数组要开在全局,1005*1005局部储存不了;

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

		}
	}

	printf("%d", a[1][1]);
}

这道题如果从上到下写要用sort进行排序;注意sort是左闭右开(a[n]+1,a[n]+1+n);

3,洛谷1048【采药】

# [NOIP2005 普及组] 采药

## 题目描述

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


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

## 输入格式

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

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

## 输出格式

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

## 样例 #1

### 样例输入 #1

```
70 3
71 100
69 1
1 2
```

### 样例输出 #1

```
3
```

## 提示

**【数据范围】**

- 对于 $30\%$ 的数据,$M \le 10$;
- 对于全部的数据,$M \le 100$。

**【题目来源】**

NOIP 2005 普及组第三题

这道题目运用了0-1背包,一维dp的思想,绝对不可以用贪心搞;

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int t, m, a[105], b[105];
int f[1005] = {0};
int main() {	
	scanf("%d %d", &t, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &a[i], &b[i]);
	}
	for (int i = 1; i <= m; i++) {
		for (int j = t; j >= a[i]; j--) {
			f[j] = max(f[j - a[i]] + b[i], f[j]);
		}
	}
	printf("%d", f[t]);

}

从第一个开始,计算出从f(t)到f(1)的最优情况,每跳动一组数都重新计算一遍;

从上往下,从右往左走;

状态转移方程:dp(i,j)=max(dp(i-1,j),dp(i-1,j-w(i)+v(i));

4,洛谷  p1616【疯狂地采药】

# 疯狂的采药

## 题目背景

此题为纪念 LiYuxiang 而生。

## 题目描述

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

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

此题和原题的不同点:

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

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

## 输入格式

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

第 $2$ 到第 $(m + 1)$ 行,每行两个整数,第 $(i + 1)$ 行的整数 $a_i, b_i$ 分别表示采摘第 $i$ 种草药的时间和该草药的价值。

## 输出格式

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

## 样例 #1

### 样例输入 #1

```
70 3
71 100
69 1
1 2
```

### 样例输出 #1

```
140
```

## 提示

#### 数据规模与约定

- 对于 $30\%$ 的数据,保证 $m \le 10^3$ 。
- 对于 $100\%$ 的数据,保证 $1 \leq m \le 10^4$,$1 \leq t \leq 10^7$,且 $1 \leq m \times t \leq 10^7$,$1 \leq a_i, b_i \leq 10^4$。

这道题是完全背包,要从左往右写,这样可以重复取药;

注意数据范围long long;

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include<cstring>
#include<algorithm>

using namespace std;
long long t, m, a[10005], b[10005];
long long f[10000005] = { 0 };
int main() {
	scanf("%lld %lld", &t, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%lld %lld", &a[i], &b[i]);
	}
	for (int i = 1; i <= m; i++) {
		for (int j = a[i]; j<=t; j++) {
			f[j] = max(f[j - a[i]] + b[i], f[j]);
		}
	}
	printf("%lld", f[t]);

}

  • 40
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值