sc2017新高二&高一模拟赛7 总结

前言:三道大水题。。。


纸牌

题目描述

纸牌选手wyz喜欢玩纸牌。
wyz有2n张纸牌,点数分别为1到2n。wyz要和你玩一个游戏,这个游戏中,每个人都会分到n张卡牌。游戏一共分为n轮,每轮你们都要出一张牌,点数大者获胜。
不自量力的wyz觉得你很菜,于是每轮他都会先于你出牌,你可以根据他出的牌来做决策。
游戏开始了,你拿到了你的牌,你现在想知道,你最多能够获胜几轮?

对于32.5%的数据,保证1<=n<=100
对于100%的数据,保证1<=n<=50,000
保证数据的合法性,即你即不会拿到重复的牌,又不会拿到超出点数范围的牌。


输入格式

第一行1个正整数n。
第2行到第n+1行每行一个正整数a[i],表示你的第i张牌的点数。


输出格式

一行一个整数表示你最多能够获胜的轮数。


输入样例

2
1
4


输出样例

1


题解(贪心+单调)

这题就是什么塞翁失马,应该是田忌赛马的加强版。

我们将手牌按小到大排序,然后用自己的牌去吃对方的牌,由于单调递增,所以时间 O(n)


代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <algorithm>
#define N 100010

using namespace std;

int n, x, ans;
int a[N], b[N];
bool vis[N];

int main(){

    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
      scanf("%d", &x);
      vis[x] = true;
    }

    for(int i = 1; i <= 2*n; i++){
      if(vis[i])  a[++a[0]] = i;
      else  b[++b[0]] = i;
    }

    for(int i = 1, j = 1; i <= n; i++){
      for(; j <= n; j++)  if(a[j] > b[i]){
        ans ++;
        j ++;
        break;
      }
    }

    printf("%d\n", ans);

    return 0;
}

杯具

题目描述

杯具选手wyz喜欢玩杯具。
wyz有2个容量分别为n单位、m单位的没有刻度的杯具。wyz有t分钟可以摆弄他的杯具。每一分钟,他都可以做下面4件事中的任意一件:
(1)用水龙头放满一个杯具。
(2)倒空一个杯具。
(3)把一个杯具里的水倒到另一个杯具里,直到一个杯具空了或者另一个杯具满了。(看哪种情况先发生)
(4)什么都不做。
wyz希望最后能获得d个单位的水,假设最后两个杯具中水量的总和为x,那么他的不开心度就为|d-x|。
现在你想知道,wyz的不开心度最小是多少。

对于10%的数据,保证t=1
对于20%的数据,保证t<=2
对于40%的数据,保证t<=4
对于100%的数据,保证1<=n,m<=100,1<=t<=100,1<=d<=200


输入格式

第一行4个整数n、m、t、d,分别表示两个杯具的容量、时间限制以及期望值。


输出格式

一行一个整数表示wyz的最小不开心度。


输入样例

7 25 2 16


输出样例

9


题解(dp/bfs)

我都不想说这题了,话说这不是普及组难度吗?一个很简单的dp,以时间为阶段,记录两个杯子分别的水量是否可以到达, f[i][j][k]=true/false 。然后直接推,最后找答案。

或者直接无脑宽搜,哪种都好, n <script type="math/tex" id="MathJax-Element-81">n</script>才100,时间完全不是问题。

然而我考试时闲得无聊,两种方法都写了,还拿来拍了半天。。。


代码(dp)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#define N 111
#define oo 0x7fffffff

using namespace std;

int n, m, T, D, ans = oo;
bool f[N][N][N];

int main(){

    scanf("%d%d%d%d", &n, &m, &T, &D);

    f[0][0][0] = true;

    for(int i = 0; i < T; i++)
     for(int c1 = 0; c1 <= n; c1++)
      for(int c2 = 0; c2 <= m; c2++){
        if(!f[i][c1][c2])  continue;
        f[i+1][c1][c2] = true;
        f[i+1][0][c2] = true;
        f[i+1][c1][0] = true;
        f[i+1][n][c2] = true;
        f[i+1][c1][m] = true;
        if(n - c1 >= c2)
          f[i+1][c1+c2][0] = true;
        else
          f[i+1][n][c2-n+c1] = true;
        if(m - c2 >= c1)
          f[i+1][0][c1+c2] = true;
        else
          f[i+1][c1-m+c2][m] = true;
      }

    for(int c1 = 0; c1 <= n; c1++)
     for(int c2 = 0; c2 <= m; c2++){
       if(!f[T][c1][c2])  continue;
       ans = min(ans, abs(c1+c2-D));
     }

    printf("%d\n", ans); 

    return 0;
}

代码(bfs)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#define N 111
#define oo 0x7fffffff

using namespace std;

int n, m, T, D, ans = oo;
bool f[N][N][N];
struct Data{
    int t, c1, c2;
    Data() {}
    Data(int _t, int _c1, int _c2):t(_t), c1(_c1), c2(_c2) {}
}q[N*N*N];
int head, tail;

int main(){
    scanf("%d%d%d%d", &n, &m, &T, &D);

    q[0] = Data(0, 0, 0);
    f[0][0][0] = true;

    while(head <= tail){
      int i = q[head].t, c1 = q[head].c1, c2 = q[head].c2;
      head ++;
      if(i == T)  continue;
      if(!f[i+1][c1][c2]){
        q[++tail] = Data(i+1, c1, c2);
        f[i+1][c1][c2] = true;
      }
      if(!f[i+1][0][c2]){
        q[++tail] = Data(i+1, 0, c2);
        f[i+1][0][c2] = true;
      }
      if(!f[i+1][c1][0]){
        q[++tail] = Data(i+1, c1, 0);
        f[i+1][c1][0] = true;
      }
      if(!f[i+1][n][c2]){
        q[++tail] = Data(i+1, n, c2);
        f[i+1][n][c2] = true;
      }
      if(!f[i+1][c1][m]){
        q[++tail] = Data(i+1, c1, m);
        f[i+1][c1][m] = true;
      }
      if(n - c1 >= c2){
        if(!f[i+1][c1+c2][0]){
          q[++tail] = Data(i+1, c1+c2, 0);
          f[i+1][c1+c2][0] = true;
        }
      }
      else{
        if(!f[i+1][n][c2-n+c1]){
          q[++tail] = Data(i+1, n, c2-n+c1);
          f[i+1][n][c2-n+c1] = true;
        }
      }
      if(m - c2 >= c1){
        if(!f[i+1][0][c1+c2]){
          q[++tail] = Data(i+1, 0, c1+c2);
          f[i+1][0][c1+c2] = true;
        }
      }
      else{
        if(!f[i+1][c1-m+c2][m]){
          q[++tail] = Data(i+1, c1-m+c2, m);
          f[i+1][c1-m+c2][m] = true;
        }
      }
    }

    for(int c1 = 0; c1 <= n; c1++)
     for(int c2 = 0; c2 <= m; c2++){
       if(!f[T][c1][c2])  continue;
       ans = min(ans, abs(c1+c2-D));
     }
    printf("%d\n", ans); 

    return 0;
}

辣鸡

题目描述

wyz在后院养了许多辣鸡。wyz的后院可以看成一个A*B的矩形,左下角的坐标为(0,0),右上角的坐标为(A,B)。
wyz还在后院里建了许多栅栏。有n个平行于y轴的栅栏a1..an,表示挡在(ai,0)到(ai,B)之间。有m个平行于x轴的栅栏b1..bn,表示挡在(0,bi)到(A,bi)之间。这样,平面被划成了(n+1)*(m+1)块辣鸡的活动区域。
为了方便辣鸡的活动,wyz现在要去掉某些栅栏的一部分,使得每一块活动区域都连通。
同时,每次修改栅栏只能去掉从某个交点到另一个交点的一整段栅栏。举(打)个比(栗)方(子):

这里写图片描述

原来是这样的布局,经过修改可以变成这样:

这里写图片描述

现在,wyz想知道,要使得每一块辣鸡活动区域都联通,最少需要去掉多少长度的栅栏。
对于10%的数据,A,B<=1000,n,m<=20
对于30%的数据,A,B<=1000,000,n*m<=25,000
对于40%的数据,n*m<=250,000
对于50%的数据,n*m<=4,000,000
对于100%的数据,1<=A,B<=1000,000,000,1<=n,m<=25,000
数据保证 0 < a[i] < A,0 < b[i] < B


输入格式

第一行4个正整数A、B、n、m,描述了后院的大小和两种栅栏的数目。
第2行到第n+1行,每行1个正整数,第i+1行的数描述了a[i]。
第n+2行到第n+m+1行,每行1个正整数,第n+i+1行的数描述了b[i]。


输出格式

一行一个整数表示需要去掉的栅栏的最小长度总和。


输入样例

15 15 5 2
2
5
10
6
4
11
3


输出样例

44


题解(MST+找规律)

这是题原题。。。
题目很明显是在求最小生成树。

转化就像下图一样:

这里写图片描述

原图中的横着的栅栏就是竖着的边,竖着的栅栏就是横着的边,每一横排、竖排的边权一样。破坏栅栏就是在连边。如果直接做Kruskal肯定超时,然而第一次做这题的时候,脑壳坏了,想不到该怎么搞,结果就直接来了。甚至我想到了排序后必然是一段一段的,但是判环还是没有想到点上。

那时随便在草稿上画了一下,发现一开始的情况和后来不太一样,觉得没什么规律可循。其实就是有规律可循啊。。。

我们发现每次必然加整一行或列,出现环就会少加一部分,行少加是受到列的影响,列也一样。行一开始整行加,后来有了两列一行后,再整行加就会有环,只能少加一个,后面随着列的增加,行越加越少,每次少一,这就发现了规律。列也是一样的(画个图比较好理解)。

于是直接将读入转成边排序,然后按着规律加边算答案。
再见,超时。


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define N 25010

using namespace std;

typedef long long LL;
LL A, B, a[N], b[N], ans;
int n, m, Rcnt, Lcnt;

struct Data{
    LL len, id;
    bool operator < (const Data& Q)const {return len < Q.len;}
}fen[N<<1];

int main(){

    scanf("%lld%lld%d%d", &A, &B, &n, &m);
    for(int i = 1; i <= n; i++)  scanf("%lld", &a[i]);
    for(int i = 1; i <= m; i++)  scanf("%lld", &b[i]);

    a[0] = 0;  a[n+1] = A;
    b[0] = 0;  b[m+1] = B;

    sort(a, a+n+2);
    sort(b, b+m+2);

    for(int i = 1; i <= n+1; i++)  fen[i].len = a[i] - a[i-1], fen[i].id = 0;
    for(int i = 1; i <= m+1; i++)  fen[i+n+1].len = b[i] - b[i-1], fen[i+n+1].id = 1;

    sort(fen+1, fen+n+m+3);

    for(int i = 1; i <= n + m + 2; i++){
      if(!fen[i].id){
        if(Rcnt >= 2 && Lcnt >= 1)  ans += fen[i].len * (m - Rcnt + 1);
        else  ans += fen[i].len * m;
        Lcnt ++;
      }
      else{
        if(Lcnt >= 2 && Rcnt >= 1)  ans += fen[i].len * (n - Lcnt + 1);
        else  ans += fen[i].len * n;
        Rcnt ++;
      }
    }

    printf("%lld\n", ans);
    return 0;
}

总结

这次比赛太水了,有十几个人AK,只有蒟蒻我还在水总结。


黑暗。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值