Codeforces Round 931 (Div. 2) A-C题解

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=156+61+12 )。而最优解却是少取一枚 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=155+102+31)。
  • (暴力枚举求法)如果赛时赶时间,根本不想找规律只想把这题快速过掉,可以尝试这个方法。看上面这个样例我们可以发现:有时 15 15 15 10 10 10 少取一点却能到最优解。那依照这个思路,我就强行在贪心的取法上对 15 15 15 10 10 10 6 6 6 3 3 3 少取 0 − 3 0 - 3 03 个,通过暴力枚举来探最优解。(可以结合代码一起看更好理解)
  • (动规(打表)+贪心)观察题目我们可以发现,这是一个完全背包问题(所有物品无限取,在取到相应数值时,重量最小)。而数据范围太大( 1 ≤ 1 0 9 1 \le 10^{9} 1109),我们需要缩小范围。 1 、 3 、 6 、 10 、 15 1、3、6、10、15 1361015 都是 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 1 , y y y),则地雷一定在 y = 1 y = 1 y=1 这条直线之上或右侧)。

  2. 我们充分利用边界,将探测点设在网格的三个角落(左上,左下,右上),其中的两个点必定指向一个地雷,而且这个地雷的坐标可以推出。如果左上和左下指向同一个地雷,这个地雷的坐标便是 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(m1))/2+1) y 1 = d 1 − x 1 + 2 y_1 = d_1 - x_1 + 2 y1=d1x1+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(n1))/2+1) x 2 = d 1 − y 2 + 2 x_2 = d_1 - y_2 + 2 x2=d1y2+2 。( d 1 d_1 d1 d 2 d_2 d2 d 3 d_3 d3 分别指左上、左下、右上离地雷的曼哈顿距离)

  3. 因为我们不能确定左上、左下、右上哪两个指向同一个地雷,我们便通过第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;
}
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值