Codeforces Round #522 (Div. 2) A~E题解
文章目录
A. Kitchen Utensils
-
题意
有 k k k个人,你需要给每个人分配一套餐具。现在剩下 n n n个餐具( a i a_i ai代表其种类),问至少被偷走多少餐具才够分。
-
解题思路
对于每个人,分到的肯定是完整的一套餐具,所以总共的餐具数量一定是整除 k k k的,更严格的说,每一个种类的餐具数量都要能整除 k k k,且题目中限制了每个种类的餐具数量都相同。所以我们只需要找到数量最多的种类餐具即可。
-
AC代码
/**
*@filename:A
*@author: pursuit
*@created: 2021-08-31 13:27
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 100 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,k,x,a[N];
void solve(){
int cnt = 0, ans = 0;
for(int i = 1; i < N; ++ i){
cnt += (a[i] != 0);
ans = max(ans,a[i]);
}
//ans需要整分给每个人。
ans = (ans + k - 1) / k * k;
printf("%d\n", cnt * ans - n);
}
int main(){
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++ i){
scanf("%d", &x);
++ a[x];
}
solve();
return 0;
}
B. Personalized Cup
-
题意
给定一个字符串,你需要将其分成一个矩形,使得行 ≤ 5 \leq 5 ≤5,列 ≤ 20 \leq 20 ≤20。你可以补充任意的
*字符,但必须满足相邻两行的*字符数量相差 ≤ 1 \leq 1 ≤1。请你构造出这样的矩形。 -
解题思路
我们可以枚举行,从而可以确定列。那么怎么确定行列才可行呢?必然是使得需要添加的
*最小最优,所以我们选择能填充的最小矩形,即 l e n / n + ( l e n % n ! = 0 ) len / n+(len \% n != 0) len/n+(len%n!=0)。那么易知,其数量不会超过行数,所以如果有我们则可以每行填一个即可。 -
AC代码
/**
*@filename:B
*@author: pursuit
*@created: 2021-08-31 13:55
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,m,len;
char s[N];
void solve(){
//确定行列即可。
len = strlen(s + 1);
for(n = 1; n <= 5; ++ n){
m = len / n + (len % n != 0);
if(n <= 5 && m <= 20)break;
}
int cnt = n * m - len, idx = 1;//需要填充的*号。
printf("%d %d\n", n, m);
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m - (cnt != 0); ++ j){
printf("%c", s[idx ++]);
}
if(cnt){
printf("*");
-- cnt;
}
puts("");
}
}
int main(){
scanf("%s", s + 1);
solve();
return 0;
}
C. Playing Piano
-
题意
给你一个 a a a序列,需要你构造出一个 b b b序列,使得:如果 a i < a i + 1 , 则 b i < b i + 1 a_i < a_{i + 1},则b_i < b_{i + 1} ai<ai+1,则bi<bi+1;如果 a i > a i + 1 , 则 b i > b i + 1 a_i > a_{i + 1},则b_i > b_{i + 1} ai>ai+1,则bi>bi+1;如果 a i = a i + 1 , 则 b i ! = b i + 1 a_i = a_{i + 1},则b_i != b_{i + 1} ai=ai+1,则bi!=bi+1。 b i b_i bi取值范围为 [ 1 , 5 ] [1,5] [1,5]。
-
解题思路
观察此题易知,当前构造的 b i b_i bi只与 b i − 1 b_{i-1} bi−1的值有关系,即由 b i − 1 b_{i-1} bi−1转移过来的。所以这个过程我们是可以通过 d p dp dp实现的,但我们无需考虑最优解,只是状态的转移计算而已。
我们可以用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个且第 i i i个填 j j j的合法情况。为 0 0 0代表不可行,否则可行。
据此题易解。注意路径的记录,我们可以用 p r e [ i ] [ j ] pre[i][j] pre[i][j]表示状态为 d p [ i ] [ j ] dp[i][j] dp[i][j]的前一个状态取的值。
-
AC代码
/**
*@filename:C
*@author: pursuit
*@created: 2021-08-31 14:06
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n,a[N];
bool dp[N][10];//dp[i][j]表示前i个且第i个填j的合法情况。为0代表不可行,否则可行。
int pre[N][10];//pre[i][j]表示该状态前一个填的是什么值。
int Stack[N],top;
void solve(){
for(int j = 1; j <= 5; ++ j){
dp[1][j] = 1;
}
for(int i = 2; i <= n; ++ i){
for(int j = 1; j <= 5; ++ j){
if(dp[i - 1][j]){
if(a[i] > a[i - 1]){
//则b[i] > b[i - 1]。
for(int k = j + 1; k <= 5; ++ k){
dp[i][k] = true, pre[i][k] = j;
}
}
else if(a[i] == a[i - 1]){
//则b[i] != b[i - 1]。
for(int k = 1; k <= 5; ++ k){
if(k != j){
dp[i][k] = true, pre[i][k] = j;
}
}
}
else{
//则b[i] < b[i - 1]。
for(int k = 1; k < j; ++ k){
dp[i][k] = true, pre[i][k] = j;
}
}
}
}
}
int x = -1;
for(int j = 1; j <= 5; ++ j){
if(dp[n][j]) x = j;//找到合法状态。
}
if(x == -1){
puts("-1");
}
else{
top = 0;
for(int i = n; i >= 1; -- i){
Stack[top ++] = x;
x = pre[i][x];
}
for(int i = top - 1; i >= 0; -- i){
printf("%d ", Stack[i]);
}
puts("");
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
solve();
return 0;
}
D. Barcelonian Distance
-
题意
给你一条直线 a x + b y + c = 0 ax+by+c=0 ax+by+c=0、起点坐标和终点坐标。你可以沿着坐标轴方向行走,在直线上你也可以沿着直线走。问从起点到终点的最短距离。
-
解题思路
当不通过直线的时候,最短距离就是曼哈顿距离。而如果通过直线,意味着我们可以走斜线,在直线上自然只有一种方式,但我们如果到达直线,如果从直线上到达终点,这里要分四种情况:起点从从y轴到达直线上或从x轴到达直线上。终点同理。我们求出在直线上的两个坐标即可。
注意特判,当a为0或者b为0的时候,此时就是一条直线,就是曼哈顿距离。
-
AC代码
/**
*@filename:D
*@author: pursuit
*@created: 2021-08-31 15:09
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
#define x first
#define y second
using namespace std;
typedef pair<double,double> pdd;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
double a,b,c;
pdd st,ed;
double getDist1(pdd a, pdd b){
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double getDist2(pdd a, pdd b){
return abs(a.x - b.x) + abs(a.y - b.y);
}
void solve(){
//不通过直线,直接曼哈顿距离。
double minn = getDist2(st,ed),ans;
//起点通过y轴到达直线,终点通过y轴到达直线。
//ax + by + c = 0,y = -(ax + c) / b, x = -(by + c) / a
if(a != 0 && b != 0){
pdd new_st(st.x,(-1) * (a * st.x + c) / b), new_ed(ed.x,(-1) * (a * ed.x + c) / b);
ans = getDist1(new_st, new_ed) + getDist1(st, new_st) + getDist1(ed, new_ed);
minn = min(ans, minn);
//起点通过y轴到达直线,终点通过x轴到达直线。
new_st = pdd(st.x,(-1) * (a * st.x + c) / b), new_ed = pdd((-1) * (b * ed.y + c) / a, ed.y);
ans = getDist1(new_st, new_ed) + getDist1(st, new_st) + getDist1(ed, new_ed);
minn = min(ans, minn);
//起点通过x轴到达直线,终点通过y轴到达直线。
new_st = pdd((-1) * (b * st.y + c) / a, st.y), new_ed = pdd(ed.x, (-1) * (a * ed.x + c) / b);
ans = getDist1(new_st, new_ed) + getDist1(st, new_st) + getDist1(ed, new_ed);
minn = min(ans, minn);
//起点通过x轴到达直线,终点通过x轴到达直线。
new_st = pdd((-1) * (b * st.y + c) / a, st.y), new_ed = pdd((-1) * (b * ed.y + c) / a, ed.y);
ans = getDist1(new_st, new_ed) + getDist1(st, new_st) + getDist1(ed, new_ed);
minn = min(ans, minn);
}
printf("%.8lf\n", minn);
}
int main(){
scanf("%lf%lf%lf", &a, &b, &c);
scanf("%lf%lf%lf%lf", &st.x, &st.y, &ed.x, &ed.y);
solve();
return 0;
}
E. The Unbearable Lightness of Weights
-
题意
有 n n n个砝码,你不知道每个砝码多重,你只知道它们有 a 1 , a 2 , a 3 … a n a_1,a_2,a_3\dots a_n a1,a2,a3…an。但是你的朋友知道,你可以每次拿出 k k k个砝码给你的朋友,他会告诉你 k k k个砝码的重量和,经过 1 1 1次尝试之后,你最多可以区分出多少个砝码?
-
解题思路
首先我们需要清楚的一件事就是我们选取砝码一定是选取砝码重量相同且砝码重量总和唯一的,这样才能保证我们唯一确定且知晓每个砝码多重。所以我们即是判定砝码重量总和唯一且砝码数量最多的是哪个?我们则可以先处理出砝码的重量种类以及对应数量。
这题很类似求 i i i个物品体积为 j j j的方案数,即分组背包问题。我们可以用 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示前 i i i个物品且体积为 j j j的方案数,若方案数唯一则可行。
据此进行状态转移即可。
注意特判砝码种类只有两个的情况,因为这我们只需要挑出最大的那个且值相同的组合,这样确定了该组,另一组也唯一确定了。
-
AC代码
/**
*@filename:E
*@author: pursuit
*@created: 2021-08-31 16:23
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e4 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
//只能选相同的值才可以确定。
int n,x,cnt[N],dp[105][N];//dp[i][j]表示前i个物品且体积为j的方案数。
vector<pii> v;
void solve(){
for(int i = 1; i <= 100; ++ i){
if(cnt[i])v.push_back({i, cnt[i]});//将占用体积和数量放入。
}
dp[0][0] = 1;
for(auto iter : v){
int w = iter.first, num = iter.second;
//cout << w << " " << num << endl;
//注意先枚举体积。
for(int j = N - 1; j > 0; -- j){
for(int i = 1; i <= n; ++ i){
for(int k = 1; k <= num && k * w <= j && k <= i; ++ k){
dp[i][j] += dp[i - k][j - k * w];
}
}
}
}
if(v.size() == 2){
//只有两种
printf("%d\n", n);
}
else{
int ans = 1;
for(auto iter : v){
int w = iter.first, num = iter.second;
for(int i = 1; i <= num; ++ i){
if(dp[i][i * w] == 1){
ans = max(ans, i);
}
}
}
printf("%d\n", ans);
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &x);
++ cnt[x];
}
solve();
return 0;
}

浙公网安备 33010602011771号