2.5
1.传纸条
2.装箱问题
3.开心的金明
4.传球游戏
5.修改数组
6.对局匹配
7.刷题统计
传纸条https://www.luogu.com.cn/problem/P1006
题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排坐成一个 �m 行 �n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1)(1,1),小轩坐在矩阵的右下角,坐标 (�,�)(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 00 表示),可以用一个 [0,100][0,100] 内的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式
第一行有两个用空格隔开的整数 �m 和 �n,表示班里有 �m 行 �n 列。
接下来的 �m 行是一个 �×�m×n 的矩阵,矩阵中第 �i 行 �j 列的整数表示坐在第 �i 行 �j 列的学生的好心程度。每行的 �n 个整数之间用空格隔开。
输出格式
输出文件共一行一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
输入输出样例
输入 #1复制
3 3 0 3 9 2 8 5 5 7 0
输出 #1复制
34
说明/提示
【数据范围】
对于 30%30% 的数据,满足 1≤�,�≤101≤m,n≤10。
对于 100%100% 的数据,满足 1≤�,�≤501≤m,n≤50。
思路:动态规划,模拟两条不相交的路径,每走一步都有四种情况:
1.第一张纸条向下传,第二张纸条向下传;
2.第一张纸条向下传,第二张纸条向右传;
3.第一张纸条向右传,第二张纸条向下传;
4.第一张纸条向右传,第二张纸条向右传;
给走的方向加一些限定,就可以形成不相交的两条路径:
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
int a[55][55],dp[55][55][55][55];
int m,n;
int maxn(int a,int b,int c,int d){
return max(a,b)>max(c,d)?max(a,b):max(c,d);
}
signed main(){
cin>>m>>n;
for (int i=1;i<=m;++i){
for (int j=1;j<=n;++j){
cin>>a[i][j];
}
}
for (int i=1;i<=m;++i){
for (int j=1;j<=n;++j){
for (int k=i+1;k<=m;++k){
for (int l=1;l<j;++l){
dp[i][j][k][l]=maxn(dp[i-1][j][k-1][l],dp[i][j-1][k][l-1],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l])+a[i][j]+a[k][l];
}
}
}
}
cout<<dp[m-1][n][m][n-1];
}
装箱问题https://www.luogu.com.cn/problem/P1049
题目描述
有一个箱子容量为 �V,同时有 �n 个物品,每个物品有一个体积。
现在从 �n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。
输入格式
第一行共一个整数 �V,表示箱子容量。
第二行共一个整数 �n,表示物品总数。
接下来 �n 行,每行有一个正整数,表示第 �i 个物品的体积。
输出格式
- 共一行一个整数,表示箱子最小剩余空间。
输入输出样例
输入 #1复制
24 6 8 3 12 7 9 7
输出 #1复制
0
说明/提示
对于 100%100% 数据,满足 0<�≤300<n≤30,1≤�≤200001≤V≤20000。
思路:动态规划,01背包问题,把v当做是背包的总共 容量,而物品的体积也是物品的价值,找到在背包容量为v的情况下,前n个物品可以形成的最大价值,然后减去背包容量就是最小剩余容量
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
int a[35],dp[20005];
int v,n;
signed main(){
cin>>v>>n;
for (int i=1;i<=n;++i){
cin>>a[i];
}
for (int i=1;i<=n;++i){
for (int j=v;j>=a[i];--j){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<<v-dp[v];
}
开心的金明https://www.luogu.com.cn/problem/P1060
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 �N 元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 �N 元。于是,他把每件物品规定了一个重要度,分为 55 等:用整数 1−51−5 表示,第 55 等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过 �N 元(可以等于 �N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第�j件物品的价格为 ��vj,重要度为 ��wj,共选中了 �k 件物品,编号依次为 �1,�2,…,��j1,j2,…,jk,则所求的总和为:
��1×��1+��2×��2…+���×���vj1×wj1+vj2×wj2…+vjk×wjk。
请你帮助金明设计一个满足要求的购物单。
输入格式
第一行,为 22 个正整数,用一个空格隔开:�,�n,m(�<30000,�<25n<30000,m<25)其中 �n 表示总钱数,�m 为希望购买物品的个数。
从第 22 行到第 �+1m+1 行,第 �j 行给出了编号为 �−1j−1 的物品的基本数据,每行有 22 个非负整数 �,�v,p(其中 �v 表示该物品的价格 (�≤10000)(v≤10000),�p 表示该物品的重要度(1≤�≤51≤p≤5)。
输出格式
11 个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000<100000000)。
输入输出样例
输入 #1复制
1000 5 800 2 400 5 300 5 400 3 200 2
输出 #1复制
3900
思路:总的背包容量为总金额,每个物品的价格就是单个物品的容量,乘上重要度就是这个物品的价值
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
int a[35],v[35],dp[30005];
int n,m;
signed main(){
cin>>n>>m;
for (int i=1;i<=m;++i){
cin>>a[i]>>v[i];
}
for (int i=1;i<=m;++i){
for (int j=n;j>=a[i];--j){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]*v[i]);
}
}
cout<<dp[n];
}
传球游戏https://www.luogu.com.cn/problem/P1057
题目描述
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的:�n 个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了 �m 次以后,又回到小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有三个同学 11 号、22 号、33 号,并假设小蛮为 11 号,球传了 33 次回到小蛮手里的方式有 1→2→3→11→2→3→1 和 1→3→2→11→3→2→1,共 22 种。
输入格式
一行,有两个用空格隔开的整数 �,�(3≤�≤30,1≤�≤30)n,m(3≤n≤30,1≤m≤30)。
输出格式
11 个整数,表示符合题意的方法数。
输入输出样例
输入 #1复制
3 3
输出 #1复制
2
说明/提示
数据范围及约定
- 对于 40%40% 的数据,满足:3≤�≤30,1≤�≤203≤n≤30,1≤m≤20;
- 对于 100%100% 的数据,满足:3≤�≤30,1≤�≤303≤n≤30,1≤m≤30。
思路:dp[i][j]表示了经过j次传球传到第i个人的次数,由于是成环的,所以第一个人和最后一个人需要特判,对于第i个人,可以有前面和后面两个人传过来,对于次数j,是由j-1次转移来的
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
int a[35],dp[35][35];
int n,m;
signed main(){
cin>>n>>m;
dp[1][0]=1;
for (int i=1;i<=m;++i){
dp[1][i]=dp[n][i-1]+dp[2][i-1];
dp[n][i]=dp[n-1][i-1]+dp[1][i-1];
for (int j=2;j<n;++j){
dp[j][i]=dp[j-1][i-1]+dp[j+1][i-1];
}
}
cout<<dp[1][m];
}
修改数组https://www.luogu.com.cn/problem/P8686
题目描述
给定一个长度为 �N 的数组 �=[�1,�2,⋯��]A=[A1,A2,⋯AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 �2,�3,⋯ ,��A2,A3,⋯,AN。
当修改 ��Ai 时,小明会检查 ��Ai 是否在 �1A1 ∼ ��−1Ai−1 中出现过。如果出现过,则小明会给 ��Ai 加上 11;如果新的 ��Ai 仍在之前出现过,小明会持续给 ��Ai 加 11,直到 ��Ai 没有在 �1A1 ∼ ��−1Ai−1 中出现过。
当 ��AN 也经过上述修改之后,显然 �A 数组中就没有重复的整数了。
现在给定初始的 �A 数组,请你计算出最终的 �A 数组。
输入格式
第一行包含一个整数 �N。
第二行包含 �N 个整数 �1,�2,⋯ ,��A1,A2,⋯,AN。
输出格式
输出 �N 个整数,依次是最终的 �1,�2,⋯ ,��A1,A2,⋯,AN。
输入输出样例
输入 #1复制
5 2 1 1 3 4
输出 #1复制
2 1 3 4 5
说明/提示
对于 80%80% 的评测用例,1≤�≤100001≤N≤10000。
对于所有评测用例,1≤�≤1051≤N≤105,1≤��≤1061≤Ai≤106。
思路:使用并查集的方法,每输入一个数a,就输出它的根节点find(a),然后把a的根节点和a的根节点+1合并起来
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
int n,f[1000005];
int find(int x){
if (f[x]==x)return x;
else{
f[x]=find(f[x]);
return f[x];
}
}
void unionn(int i,int j){
f[find(i)]=find(j);
}
signed main(){
cin>>n;
for (int i=1;i<=100005;++i){
f[i]=i;
}
for (int i=1;i<=n;++i){
int x;
cin>>x;
cout<<find(x)<<" ";
unionn(find(x),find(x)+1);
}
}
对局匹配https://www.luogu.com.cn/problem/P8656
题目描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是 �K 的两名用户匹配在一起。如果两人分差小于或大于 �K,系统都不会将他们匹配。
现在小明知道这个网站总共有 �N 名用户,以及他们的积分分别是 �1,�2,⋯��A1,A2,⋯AN。
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于 �K)?
输入格式
第一行包含两个个整数 �N 和 �K。
第二行包含 �N 个整数 �1,�2,⋯ ,��A1,A2,⋯,AN。
输出格式
一个整数,代表答案。
输入输出样例
输入 #1复制
10 0 1 4 2 8 5 7 1 4 2 8
输出 #1复制
6
输入 #2复制
10 1 2 1 1 1 1 4 4 3 4 4
输出 #2复制
8
说明/提示
对于 30%30% 的数据,1≤�≤101≤N≤10。
对于 100%100% 的数据,1≤�≤1051≤N≤105,0≤�,��≤1050≤K,Ai≤105 。
思路:把所有积分相同的人数加起来,并判断两种情况k是否等于0
如果等于0,那么记录积分的种类,就是答案,因为每个种类的积分就放一个人,是不能比赛的
如果不等于0,就把相差k的积分的人数相减,最后加起来就是答案
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
const int N=1e5+5;
int n,k,a[N],s[N];
signed main(){
cin>>n>>k;
for (int i=0;i<n;++i){
int x;
cin>>x;
s[x]++;
}
if (k==0){
int cnt=0;
for (int i=0;i<=N;++i){
if (s[i]!=0)cnt++;
}
cout<<cnt;
return 0;
}else if (k!=0){
for (int i=0;i<=N-k;++i){
if (s[i]<s[i+k]) s[i+k]-=s[i];
else s[i+k]=0;
}
}
int cnt=0;
for (int i=0;i<N;++i){
cnt+=s[i];
}
cout<<cnt;
}
刷题统计https://www.luogu.com.cn/problem/P8780
题目描述
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 �a 道题目,周六和周日每天做 �b 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 �n 题?
输入格式
输入一行包含三个整数 �,�a,b 和 �n.
输出格式
输出一个整数代表天数。
输入输出样例
输入 #1复制
10 20 99
输出 #1复制
8
说明/提示
对于 50%50% 的评测用例,1≤�,�,�≤1061≤a,b,n≤106.
对于 100%100% 的评测用例,1≤�,�,�≤10181≤a,b,n≤1018.
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
#define int long long
signed main(){
int n,m,k;
cin>>n>>m>>k;
int week=n*5+m*2;
int day=(k/week)*7;
k %=week;
if (k<=n*5) day+=k/n+(k%n==0 ? 0:1);
else {
day+=5; k-=5*n;
day+=k/m+(k%m==0 ? 0:1);
}
cout<<day;
}