背包
0-1背包
有n个物品,第i个物品的体积为w[i],价值为v[i],每个物品之多选一个,体积和不超过capacity时的最大价值和。
回溯三问
- 当前操作?枚举第i个物品选/不选:不选,容量不变;选,剩余容量减少w[i]
- 子问题?在剩余容量为c时,从前 i个物品中得到的最大价值和
- 下一个子问题?分类讨论:不选:在剩余容量为c时,从前i-1个物品中得到的的最大价值和;选:在剩余容量为c-w[i]时,从前i-1个物品中得到的最大价值和
dfs(i,c)=max(dfs(i-1,c),dfs(i-1,c-w[i])+v[i])
//背包
//记忆化搜索
const int N = 100000;
int cache[N], nums[N], w[N], v[N];
int n, capacity; //一共有n个物品,背包空间为capacity
int dfs(int i, int c)
{
if (i < 0)
return 0;
if (c < w[i])
return dfs(i - 1, c);
return max(dfs(i - 1, c), dfs(i - 1, c - w[i]) + v[i]);
return dfs(n - 1, capacity);
}
练习题
P9198 「GMOI R2-T1」轴对称
# 「GMOI R2-T1」轴对称
## 题目描述
你有一个 $n$ 行 $m$ 列的图片(矩阵),该图片的像素为 $n\times m$。
初始时,所有像素块均为黑色,RGB 是 $(0,0,0)$。每一次操作可以将一个像素块的 RGB 中的一个数字改变。
在每次操作过后,请你输出图片是否左右对称?
左右对称:即对于任何的 $i,j$,总满足第 $i$ 行第 $j$ 列的像素与第 $i$ 行第 $m-j+1$ 列的像素的 RGB 值相等。
## 输入格式
第一行三个整数 $n,m,q$,$q$ 代表操作次数。
接下来 $q$ 行,每行输入四个整数 $i,j,t,c$,表示将第 $i$ 行第 $j$ 列的格子的 RGB 值的第 $t$ 个数增加 $c$,任何一个 RGB 值的任何一个数如果超出 $255$ 则自动对 $256$ 取模。
## 输出格式
每次操作过后,如果图片左右对称,输出 `Yes`,否则输出 `No`。每组询问的输出之间用换行隔开。
## 样例 #1
### 样例输入 #1
```
6 6 9
1 2 3 4
5 6 3 4
1 5 3 4
5 1 3 4
1 3 2 260
1 4 2 4
2 2 3 5
2 5 3 7
2 2 3 258
```
### 样例输出 #1
```
No
No
No
Yes
No
Yes
No
No
Yes
```
#include<bits/stdc++.h>
using namespace std;
int n, m, a[100][100][4] = { 0 }, q;
int main()
{
cin >> n >> m >> q;
while (q--)
{
int x, y, t, c, f = 1;
cin >> x >> y >> t >> c;
c %= 256;
a[x][y][t] += c;
a[x][y][t] %= 256;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int k = 1; k <= 3; k++)
{
if (a[i][m - j + 1][k] != a[i][j][k])
{
f = 0;
break;
}
}
if (!f)
break;
}
if (!f)
break;
}
if (!f)
puts("No");
else
puts("Yes");
}
return 0;
}
P5635 【CSGRound1】天下第一
# 【CSGRound1】天下第一
## 题目背景
天下第一的 cbw 以主席的身份在 8102 年统治全宇宙后,开始了自己休闲的生活,并邀请自己的好友每天都来和他做游戏。由于 cbw 想要显出自己平易近人,所以 zhouwc 虽然是一个蒟蒻,也有能和 cbw 玩游戏的机会。
## 题目描述
游戏是这样的:
给定两个数 $x$,$y$,与一个模数 $p$。
cbw 拥有数 $x$,zhouwc 拥有数 $y$。
第一个回合:$x\leftarrow(x+y)\bmod p$。
第二个回合:$y\leftarrow(x+y)\bmod p$。
第三个回合:$x\leftarrow(x+y)\bmod p$。
第四个回合:$y\leftarrow(x+y)\bmod p$。
以此类推....
如果 $x$ 先到 $0$,则 cbw 胜利。如果 $y$ 先到 $0$,则 zhouwc 胜利。如果 $x,y$ 都不能到 $0$,则为平局。
cbw 为了捍卫自己主席的尊严,想要提前知道游戏的结果,并且可以趁机动点手脚,所以他希望你来告诉他结果。
## 输入格式
有多组数据。
第一行:$T$ 和 $p$ 表示一共有 $T$ 组数据且模数都为 $p$。
以下 $T$ 行,每行两个数 $x,y$。
## 输出格式
共 $T$ 行
$1$ 表示 cbw 获胜,$2$ 表示 zhouwc 获胜,```error``` 表示平局。
## 样例 #1
### 样例输入 #1
```
1 10
1 3
```
### 样例输出 #1
```
error
```
## 样例 #2
### 样例输入 #2
```
1 10
4 5
```
### 样例输出 #2
```
1
```
## 提示
$1 \leq T \leq 200$。
$1 \leq x,y,p \leq 10000$。
#include<bits/stdc++.h>
using namespace std;
int t, x, y, mod;
short book[10010][10010];
int func(int x, int y)
{
if (book[x][y] == -1) return -1;
if (book[x][y]) return book[x][y];
book[x][y] = -1;
if (!x) return book[x][y] = 1;
if (!y) return book[x][y] = 2;
int num = (x + y) % mod;
return book[x][y] = func(num, (num % mod + y) % mod);
}
int main()
{
scanf("%d %d", &t, &mod);
for (int i = 0; i < t; i++)
{
scanf("%d %d", &x, &y);
int ans = func(x, y);
if (ans == -1) puts("error");
else if (ans == 1) puts("1");
else puts("2");
}
return 0;
}
P3367 【模板】并查集
# 【模板】并查集
## 题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
## 输入格式
第一行包含两个整数 $N,M$ ,表示共有 $N$ 个元素和 $M$ 个操作。
接下来 $M$ 行,每行包含三个整数 $Z_i,X_i,Y_i$ 。
当 $Z_i=1$ 时,将 $X_i$ 与 $Y_i$ 所在的集合合并。
当 $Z_i=2$ 时,输出 $X_i$ 与 $Y_i$ 是否在同一集合内,是的输出
`Y` ;否则输出 `N` 。
## 输出格式
对于每一个 $Z_i=2$ 的操作,都有一行输出,每行包含一个大写字母,为 `Y` 或者 `N` 。
## 样例 #1
### 样例输入 #1
```
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
```
### 样例输出 #1
```
N
Y
N
Y
```
## 提示
对于 $30\%$ 的数据,$N \le 10$,$M \le 20$。
对于 $70\%$ 的数据,$N \le 100$,$M \le 10^3$。
对于 $100\%$ 的数据,$1\le N \le 10^4$,$1\le M \le 2\times 10^5$,$1 \le X_i, Y_i \le N$,$Z_i \in \{ 1, 2 \}$。
#include<bits/stdc++.h>
using namespace std;
int f[10000], p1, p2, p3;
int getf(int v)
{
if (f[v] == v)
return v;
else
{
f[v] = getf(f[v]);
return f[v];
}
}
int main()
{
int n = 0, m = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
{
cin >> p1 >> p2 >> p3;
if (p1 == 1)
{
f[getf(p2)] = getf(p3);
}
else
{
if (getf(p2) == getf(p3))
cout << 'Y' << endl;
else
cout << 'N' << endl;
}
}
return 0;
}
P1111 修复公路
# 修复公路
## 题目背景
A 地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。
## 题目描述
给出 A 地区的村庄数 $N$,和公路数 $M$,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)。
## 输入格式
第 $1$ 行两个正整数 $N,M$。
下面 $M$ 行,每行 $3$ 个正整数 $x,y,t$,告诉你这条公路连着 $x,y$ 两个村庄,在时间t时能修复完成这条公路。
## 输出格式
如果全部公路修复完毕仍然存在两个村庄无法通车,则输出 $-1$,否则输出最早什么时候任意两个村庄能够通车。
## 样例 #1
### 样例输入 #1
```
4 4
1 2 6
1 3 4
1 4 5
4 2 3
```
### 样例输出 #1
```
5
```
## 提示
$1\leq x, y\leq N \le 10 ^ 3$,$1\leq M, t \le 10 ^ 5$。
#include<bits/stdc++.h>
using namespace std;
struct edge
{
int fr, to, len;
}ed[20000000];
int n, m, x, y, t, cnt, maxn;
int f[100000];
bool cmp(edge a, edge b)
{
return a.len < b.len;
}
int getf(int v)
{
if (f[v] == v)
return v;
else
{
f[v] = getf(f[v]);
return f[v];
}
}
void merge(int u, int v)
{
int f1 = getf(u);
int f2 = getf(v);
if (f1 != f2)
{
f[f2] = f1;
}
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
f[i] = i;
}
for (int i = 1; i <= m; i++)
{
cin >> ed[i].fr >> ed[i].to >> ed[i].len;
}
sort(ed + 1, ed + m + 1, cmp);
for (int i = 1; i <= m; i++)
{
if (cnt == n - 1)
break;
else if (getf(ed[i].fr) == getf(ed[i].to))
continue;
merge(ed[i].fr, ed[i].to);
maxn = max(maxn, ed[i].len);
cnt++;
}
if (cnt == n - 1)
cout << maxn;
else
cout << -1;
return 0;
}
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$。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5, M = 1e7 + 5;
int w[N], v[N], n, m;
int f[M];
signed main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
cin >> w[i] >> v[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = w[i]; j <= m; j++)
{
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
}
cout << f[m];
return 0;
}
P1048 [NOIP2005 普及组] 采药
# [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 普及组第三题
#include<bits/stdc++.h>
using namespace std ;
int w[1005] , v[1005] ;
int f[1005][1005] ;
int main()
{
int t , m ;
cin >> t >> m ;
for(int i = 1 ; i <= m ; ++i)
{
cin >> w[i] >> v[i] ;
for(int j = 1 ; j <= t ; ++j)
{
f[i][j] = f[i - 1][j] ;
if(j >= w[i])
f[i][j] = max(f[i][j], f[i - 1][j - w[i]] + v[i]) ;
}
}
cout << f[m][t] ;
return 0 ;
}
P1115 最大子段和
# 最大子段和
## 题目描述
给出一个长度为 $n$ 的序列 $a$,选出其中连续且非空的一段使得这段和最大。
## 输入格式
第一行是一个整数,表示序列的长度 $n$。
第二行有 $n$ 个整数,第 $i$ 个整数表示序列的第 $i$ 个数字 $a_i$。
## 输出格式
输出一行一个整数表示答案。
## 样例 #1
### 样例输入 #1
```
7
2 -4 3 -1 2 -4 3
```
### 样例输出 #1
```
4
```
## 提示
#### 样例 1 解释
选取 $[3, 5]$ 子段 $\{3, -1, 2\}$,其和为 $4$。
#### 数据规模与约定
- 对于 $40\%$ 的数据,保证 $n \leq 2 \times 10^3$。
- 对于 $100\%$ 的数据,保证 $1 \leq n \leq 2 \times 10^5$,$-10^4 \leq a_i \leq 10^4$。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int main()
{
int nums[N], dp[N];
int n = 0, ans = INT_MIN;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> nums[i];
}
dp[0] = nums[0];
for (int i = 0; i < n; i++)
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
if (ans < dp[i])
ans = dp[i];
}
cout << ans;
}