AtCoder Beginner Contest 203(Sponsored by Panasonic)

AtCoder Beginner Contest 203(Sponsored by Panasonic)

导读:
简单的题目,只说明题意,或者直接说明结论
下面的难题,会做详细的解释和证明

A Chinchirorin

题意为:若有两个执行相同,输出剩下的那一个,否则,输出0

int a[N];
void work()
{
  for (int i = 0; i < 3; i ++ )
    cin >> a[i];

  for (int i = 0; i < 3; i ++ )
    for (int j = i + 1; j < 3; j ++ )
      if (a[i] == a[j])
      {
        cout << a[3 - i - j] << endl;
        return;
      }
  cout << 0 << endl;
}

B AtCoder Condominium

将每层楼的门牌号都加起来

int n, k;
void work()
{
  cin >> n >> k;

  int sum = 0;
  for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= k; j ++ )
    {
      sum += i * 100 + j;
    }
  cout << sum << endl;
}

C Friends and Travel costs

理解:体力有多少,就对应着能走多元呗。体力没了,寸步难行

LL n, k;
struct Node{
  LL a, b;
 
  bool operator< (const Node &W) const {
    if (a == W.a) return b > W.b;
    return a < W.a;
  }
}a[N];
void work()
{
  cin >> n >> k;
  for (int i = 0; i < n; i ++ )
    scanf("%lld%lld", &a[i].a, &a[i].b);
 
  sort(a, a + n);
 
  LL len = 0;
  for (int i = 0; i < n; i ++ )
  {
    if (k >= a[i].a) k = k + a[i].b;
  }
 
  cout << k << endl;
}

D Pond

第(K * K / 2 + 1)大的数最小化,可以抽象为最大值最小化问题,用二分来做。
我们二分一个数为x,如果大于x就令值为1,如果值小于或等于x就令值为0.然后求一个前缀和,暴力枚举每个kk矩形,O(1)查询是否存在小于(K*K/2+1)的情况,如果存在,r=mid,否则,l=mid
证明:如果出现小于情况,说明至少存在一个数使得当前满足当前k
k的方格内出现了可以比x小或等于x的数,试的它是当前KK方格的第(KK/2+1)大的格子。而其他格子,最起码都要大于等于(K * K / 1+ 1)说明,他们的第(K * K /2)大的数字一定要大于当前的x,而当前的x是合法的,当然也可以更小。我们可以接着这样二分下去,最后可以确定一个恰好的x,使得它至少是一个KK区域的第(KK/2+1)大数,然后,其他的格子的第(K*K/2+1)大数都要大于x。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <limits.h>
#include <sstream>
#include <cctype>
#include <numeric>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <set>
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inlin")

using namespace std;

#define ios ios::sync_with_stdio(false) , cin.tie(0)
#define x first
#define y second

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;

const int N = 810, INF = 0x3f3f3f3f, mod = 1e9 + 7, base = 131;
const double eps = 1e-6, PI = acos(-1);

int n, k;
int g[N][N];
int s[N][N];
int trick;

bool check(int mid)
{
  for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= n; j ++ )
      if (g[i][j] > mid) s[i][j] = 1;
      else s[i][j] = 0;

  for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= n; j ++ )
      s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

  for (int i = 1; i <= n - k + 1; i ++ )
    for (int j = 1; j <= n - k + 1; j ++ )
    {
      int l = i + k - 1, r = j + k - 1;
      int sum = s[l][r] - s[i - 1][r] - s[l][j - 1] + s[i - 1][j - 1];
      if (sum < trick) return true;
    }
  return false;
}
void work()
{
  cin >> n >> k;

  trick = k * k / 2 + 1;
  int r = 0, l = 1e9;
  for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= n; j ++ )
    {
      scanf("%d", &g[i][j]);
      l = min(l, g[i][j]);
      r = max(r, g[i][j]);
    }

  while (l < r)
  {
    int mid = l + r >> 1;

    bool f = check(mid);
    // printf("mid = %d, f = %d\n", mid, f);
    if (f) r = mid;
    else l = mid + 1;
  }

  cout << r << "\n";
}

int main()
{
  //ios;
  int T = 1;
  // cin >> T;
  while (T -- )
  {
    work();
  }

  return 0;
}

E White Pawn

题意为:初始时有一个白棋子在(0,n)的位置,在棋盘上散布着m个黑棋子。
下面是移动规则:
规则1. 若当前白棋子在位置(i,j), 若在位置(i + 1, j - 1)有黑子,则白子可以到达(i + 1, j - 1)这个位置,如果位置(i + 1, j + 1)有黑子,则白字可到达(i+ 1, j + 1)
规则2.如果位置(i + 1, j)没有黑子,则白字可以到达(i+ 1, j)

做法:我们可以看到n是1e9大的数字,而m是要给2e5的数字,所以,中间肯定有很多行是空白的。那么,根据规则2,中间的空白可以直接跳过。
设集合S是我们每一行可达位置。
若第i行是空行,则,Si-1 = Si
那么我们使用一个S,每次转移的时候,若是空格可以直接到,我们就不管了,我们就看那一列可以新添加,那一列不可达必须要去掉,用set来存储,可以自动帮我们去重,最后的答案就是set的size
若第i行不是空行,根据规则1,我们可以得到若是(i, j)这个位置有黑子,那么如果(i-1,j-1)或者(i-1,j+1)是可达的,但是(i-1,j-1)是不可达的,则Si-1可以达到Si的(i,j)位置。(错位的我们默认可以能是新添加的)放到a数组中
若位置(i,j)是黑棋子,而且(i-1,j-1)和(i-1,j+1)都是空的,但是(i-1,j)是空格,那么一定不能到达位置(i,j),我们用b来存储。
每次,将a插入S,每次将b从S中去掉。
下面是代码:

#include <bits/stdc++.h>

using namespace std;

int main(void){
  int n,m; cin >> n >> m;
  vector<pair<int, int> > v;
  for (int i = 0; i < m; i ++ )
  {
    int x, y; scanf("%d%d", &x, &y);
    v.push_back({x, y});
  }
  sort(v.begin(), v.end());
  int l = 0, r = 0;
  set<int> s;
  vector<int> a, b;
  s.insert(n);
  while (l < m)
  {
    a.clear();
    b.clear();
    for(r = l; r < m && v[r].first == v[l].first; r ++ );
    for (int i = l; i < r; i ++ )
    {
      int y = v[i].second;
      if ((s.find(y - 1) != s.end() || s.find(y + 1) != s.end()))
        a.push_back(y);
      if ((s.find(y - 1) == s.end() && s.find(y + 1) == s.end()) && s.find(y) != s.end())
        b.push_back(y);
    }
    for (int it : a) s.insert(it);
    for (int it : b) s.erase(it);
    l = r;
  }

  cout << s.size() << "\n";
}

F Weed

题意:设第一个人是女生,第二个人是男生。
题目中,女生,男生都可以做相应的操作
女生的操作:女生可以最多操作k次,每次可以除去任何一棵草。
男生的操作:设当前没被铲除的草的最高高度为H,男生可以铲除当前所有高度大于H/2的所有草。
题目要求:让我们求在男生操作次数最少的情况下,女生操作次数也要最小,将所有杂草都给铲除掉。

做法:DP求解
第一步无脑排序,因为草的排布对答案无影响,升序拍个序。
然后思考
dp[i][j]的意义,从第一颗草开始铲除一直铲除到了当前的第i棵草,男生操作了j次,女生最少操作的次数为dp[i][j]
那么对于每次的dp[][j]有两种做法,第一就是女生操作,那就是dp[i][j] = dp[i - 1][j] + 1,第二就是男生操作,结果为
dp[i][j] = dp[i - 1][j -1]我们去一个min。
再考虑边界问题,对于i=0的时候,dp[0][j]都是0
对于i>=1的时候,dp[i][0] = i
代码:

#include <bits/stdc++.h>

using namespace std;

#define N 200010
#define rep(i, n) for(int i = 0; i < n; ++ i)

int n, k;
int a[N];
int dp[N][31];
int b[N];
/*
题意:女的先选择至多k个草给消灭掉,然后剩下的交给男的来操作,男的每次
可以消灭比当前最高的草的二分之一还高的草都给消灭掉。
要求我们求出能将所有草给消灭掉的情况下,男孩操作最少,在男孩操作数相同
的情况下,要让女孩操作数达到最少。
dp[i][j]的含义是,从第0跟草消灭到了第i根草的时候,男孩操作了j次,女孩最少要操作的次数(dp[i][j]的值)
 */
int main(void)
{
  cin >> n >> k;
  rep(i, n) cin >> a[i];
  sort(a, a + n);
  int cur = 0;
  //b数组表示,以当前位置i的草作为最高的草,可以消灭一个区间的草,这个区间的左区间为b[i],右区间为i
  rep(i, n){
    while (a[cur] <= (a[i] / 2)) cur ++;
    b[i] = cur;
  }
  // rep(i, n) printf("%3d ", a[i]); puts("");
  // rep(i, n) printf("%3d ", b[i]); puts("");
  rep(i, n){
    dp[i + 1][0] = dp[i][0] + 1;//如果只让女孩来消灭,那么优即可就要消灭几次
    rep(j, 30){
      //要么是男孩不操作,所以第二维为j+1,然后女孩操作,在消灭i的基础上+1消灭掉第i+1的草
      //要么是女孩不操作,男孩在从b[i]的位置(可以消灭掉第i草的最早的位置)操作一次,j+1由j转移而来
      dp[i + 1][j + 1] = min(dp[i][j + 1] + 1, dp[b[i]][j]);
    }
  }

  rep(j, 31){
    if (dp[n][j] <= k) {
      cout << j << " " << dp[n][j] << endl;
      return 0;
    }
  }
  return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值