Codeforces Round 931 (Div. 2) A-C题解
A. Too Min Too MAX Problem - A - Codeforces
题目类型:贪心
解题思路:
由题目中的两点差绝对值联想,我们可以想到两点在一条直线上的距离。题目要求我们的绝对值和最大,我们就取数组中最大的两个和最小的两个值,每个绝对值中都是一个最大的减一个最小的即可。而求数组中最大和最小的两个值,我们便可以对数组排序来获取到。
AC代码:
void solve() {
// code here
int n;
cin >> n;
vector<int>arr(n);
for(int i = 0;i < n;++i)
cin >> arr[i];
sort(arr.begin(),arr.end());
cout << abs(arr[0] - arr[n - 1]) + abs(arr[n - 1] - arr[1]) + abs(arr[1] - arr[n - 2]) + abs(arr[n - 2] - arr[0]) << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B. Yet Another Coin Problem Problem - B - Codeforces
题目类型:贪心,数论,暴力 。
解题思路:
这里提供两个解题思路,分别是省时间少动脑的暴力和找规律得正解的动规+贪心。
- 观察样例我们可以发现,用贪心一直取 15 15 15 或 10 10 10 反而会用到更多枚的硬币。 如 98 98 98 一直取 15 15 15 的硬币,那么一共需要 9 9 9 枚硬币( 98 = 15 ∗ 6 + 6 ∗ 1 + 1 ∗ 2 98 = 15 * 6 + 6 * 1+ 1 * 2 98=15∗6+6∗1+1∗2 )。而最优解却是少取一枚 15 15 15 的硬币,而取一枚 3 3 3 和两枚 10 10 10 凑成 98 98 98 ,一共只需要 $8 $ 枚硬币( 98 = 15 ∗ 5 + 10 ∗ 2 + 3 ∗ 1 98 = 15 * 5 + 10 * 2 + 3 * 1 98=15∗5+10∗2+3∗1)。
- (暴力枚举求法)如果赛时赶时间,根本不想找规律只想把这题快速过掉,可以尝试这个方法。看上面这个样例我们可以发现:有时 15 15 15 和 10 10 10 少取一点却能到最优解。那依照这个思路,我就强行在贪心的取法上对 15 15 15 、 10 10 10 、 6 6 6 或 3 3 3 少取 0 − 3 0 - 3 0−3 个,通过暴力枚举来探最优解。(可以结合代码一起看更好理解)
- (动规(打表)+贪心)观察题目我们可以发现,这是一个完全背包问题(所有物品无限取,在取到相应数值时,重量最小)。而数据范围太大( 1 ≤ 1 0 9 1 \le 10^{9} 1≤109),我们需要缩小范围。 1 、 3 、 6 、 10 、 15 1、3、6、10、15 1、3、6、10、15 都是 30 30 30 的因数,所以 30 30 30 为一周期循环。我们可以先把 30 30 30 以外的数部分都用 15 15 15 来取 ,到 30 30 30 之内再通过背包(也可打表)来取。
AC代码:
暴力枚举求法
//
// Created by Mrlaolu on 2024/3/1.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
int temp = 0;
int div1[6] = {1,3,6,10,15};
int n,n1;
int cnt = INT_MAX;
void dfs(int n) //通过回溯法来暴力枚举少取0 - 3枚的情况
{
if(n == 4){temp = 0;}
if(n == 0) //最后要通过 1 来兜底,保证总和取到 n
{
temp += n1 / div1[n];
cnt = MIN(cnt,temp);
temp -= n1 / div1[n];
return;
}
for(int j = 0;j < 3 && n1 / div1[n] >= j;++j) //注意少取的数值不能大于 n
{
int n2 = n1;
temp += n1 / div1[n];
n1 %= div1[n];
if(n != 0)
{
n1 += div1[n] * j;
temp -= j;
dfs(n - 1);
temp += j;
n1 -= div1[n] * j;
}
n1 = n2;
temp -= n1 / div1[n];
}
}
void solve() {
// code here
cnt = INT_MAX;
cin >> n;
n1 = n;
dfs(4);
cout << cnt << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
动规(打表)+贪心
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
int dp[40][6] = {0};
int val[6] = {1,3,6,10,15};
void init_dp() //这部分可手工打表实现,前置知识:完全背包
{
for(int j = 1;j <= 30;++j) //对只取 1 那行的初始化
{
dp[j][0] = j;
}
for (int i = 1; i < 5; ++i)
{
for (int j = 1; j <= 30; ++j)
{
if (j >= val[i])
{
dp[j][i] = MIN(dp[j][i - 1], dp[j - val[i]][i] + 1);
} else
{
dp[j][i] = dp[j][i - 1];
}
}
}
}
void solve() {
// code here
int n;
cin >> n;
int ans = INT_MAX;
int t1 = n / 15,t2 = n % 15;
for(int i = t2;i <= 30 && i <= n;i += 15) { //因为很难控制最后留下来的刚好在(15,30)之内,所以通过循环简单再+一点来控制
ans = min(ans, t1 + dp[i][4]);
t1--;
}
cout << ans << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
init_dp();
while (t--) {
solve();
}
return 0;
}
C. Find a Mine Problem - C - Codeforces
题目类型:交互
解题思路:
-
因为绝对值的原因,我们无法知道地雷在探测点的哪个方向。而我们可以选择把检测点放边上,通过边界来帮我们确定地雷在探测点的大致方位(如选择( 1 1 1 , y y y),则地雷一定在 y = 1 y = 1 y=1 这条直线之上或右侧)。
-
我们充分利用边界,将探测点设在网格的三个角落(左上,左下,右上),其中的两个点必定指向一个地雷,而且这个地雷的坐标可以推出。如果左上和左下指向同一个地雷,这个地雷的坐标便是 x 1 = m i n ( 1 , ( d 1 + d 2 − ( m − 1 ) ) / 2 + 1 ) x_1 = min(1,(d_1 + d_2 - (m - 1)) / 2 + 1) x1=min(1,(d1+d2−(m−1))/2+1) y 1 = d 1 − x 1 + 2 y_1 = d_1 - x_1 + 2 y1=d1−x1+2 。如果左上和右上指向同一个地雷,这个地雷的坐标便是 y 2 = m a x ( 1 , ( d 1 + d 3 − ( n − 1 ) ) / 2 + 1 ) y_2 = max(1,(d_1 + d_3 - (n - 1)) / 2 + 1) y2=max(1,(d1+d3−(n−1))/2+1) x 2 = d 1 − y 2 + 2 x_2 = d_1 - y_2 + 2 x2=d1−y2+2 。( d 1 d_1 d1、 d 2 d_2 d2、 d 3 d_3 d3 分别指左上、左下、右上离地雷的曼哈顿距离)
-
因为我们不能确定左上、左下、右上哪两个指向同一个地雷,我们便通过第4个去探测地雷可能的位置的中的一个(比如 ( x 1 , y 1 ) (x_1,y_1) (x1,y1))。如果这个是坐标是地雷,则返回距离为 0 0 0 ,返回这个位置。反之另外一个坐标是地雷。
AC代码:
#include <bits/stdc++.h>
#define int long long
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
const int N = 1010;
const int MOD = 1010;
long long qpow(long long a, long long b);
inline int ask(int x,int y)
{
cout << "? " << x << " " << y << endl;
int feedback;
cin >> feedback;
return feedback;
}
void solve() {
int n,m;
cin >> n >> m;
int d1 = ask(1,1);
int d2 = ask(1,m);
int d3 = ask(n,1);
int x1 = MAX(1,(d1 + d2 - (m - 1)) / 2 + 1);
int y1 = d1 - x1 + 2;
int y2 = MAX(1,(d1 + d3 - (n - 1)) / 2 + 1);
int x2 = d1 - y2 + 2;
if(ask(x1,y1) == 0)
{cout << "! " << x1 << " " << y1 << endl;}
else
{cout << "! " << x2 << " " << y2 << endl;}
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}