上午
考试  ### 第一题
一句话题意:给你几堆火,给你几个罐子,这几个罐子分别对火可能会产生不同的影响,每次使用罐子的时候都必须对全部的火进行操作,\(1\)表示浇灭,\(0\)表示 无影响,\(-1\) 表示点燃
问最少用几次罐子可以把火浇灭,否则输出“-1”
一眼搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
   | #include <bits/stdc++.h> #define LL long long #define debug using namespace std;
  const int N = 1e3+250;
  queue<int>q; map<LL, int> vis, dis;  int n, m; int zuikaishideyangzi, moweideyangzi; int a[250][250];
  inline void shuruyijichushihua() {    cin >> n >> m;    for (int i = 1; i <= n; ++ i) zuikaishideyangzi = zuikaishideyangzi * 10 + 1;    dis[zuikaishideyangzi] = 0;    vis[zuikaishideyangzi] = 1;    for (int i = 1; i <= m; ++ i) {       for (int j = 1; j <= n; ++ j) {          cin >> a[i][j];       }    }    q.push(zuikaishideyangzi);    return; }
  inline void jiejuewenti() {    while (q.size()) {       int now = q.front(); q.pop();       if (now == moweideyangzi) return;       int tmp = n, shu[66], yuan[66];       int xianzaideyangzi = now;       while (tmp != 0) {          yuan[tmp] = shu[tmp] = xianzaideyangzi%10;          -- tmp;          xianzaideyangzi /= 10;       }       for (int i = 1; i <= m; ++ i) {          for (int j = 1; j <= n; ++ j) {             if (a[i][j] == 1) shu[j] = 0;                if (a[i][j] == -1) shu[j] = 1;          }          LL houlainayiwei = 0;          for (int j = 1; j <= n; ++ j) {          houlainayiwei = houlainayiwei*10 + shu[j];          shu[j] = yuan[j];          }          if (!vis.count(houlainayiwei)) {             vis[houlainayiwei] = 1;             dis[houlainayiwei] = dis[now] + 1;             q.push(houlainayiwei);          }       }    } }
  inline void shuchudaan() {    if (vis[moweideyangzi]) cout << dis[moweideyangzi];    else puts ("-1");    return; }
  inline int thestars() {    shuruyijichushihua();    jiejuewenti();    shuchudaan();    return 0; }
  int youngore = thestars();
  signed main() {;}
   | 
 
第二题
photo
一眼贪心,我们观察样例以及样例解释
Code
3 1
2 2
4 3
样例解释:
如果没人躺过来,需要27的面积。
我们只要让第1个人躺过来,就只需要21的面积!
 
开始揣测,为什么一定要让第一个人的\(w\)与\(h\)翻转?为什么不是别人?如果别人翻转会是什么样子的?
画图发现
在第一个人的\(w\)与\(h\)翻转前后,他们的\(maxh\)一直都是3,只是他们的\(sumw\)变小了,所以整体的体积变小了
在经过一段时间的臆想,我决定给他们按某种方式的排序,或许会好做一些
考虑:按\(h\)的高度大小排序?显然不合适,没有考虑全体,缺点太多
考虑:按\(w\)的大小排序?显然要被否定
于是继续臆想,最终决定按一个数的\(h\)与\(w\)的差值来从大到小来排序(他们的绝对值)
于是原来的样例变成这样:
Code
3 1(3-1=2)
4 3(4-3=1)
2 2(2-2=0)
 
我们肯定优先是对那些\(h\)与\(w\)差值大的组合进行翻转,因为只有这样才有可能对答案产生有效或者更大的贡献
这时候再来考虑:
\(h\)与\(w\)大小的关系
- \(w > h\)
 
- \(w < h\)
 
- \(w = h\)
 
显然对于第三种情况我们无论是否调换\(w\)\(h\)都是无影响的
分情况讨论第一种与第二种情况:
当\(w>h\)的时候,考虑\(w\)是否影响\(maxh\)
1.\(w <maxh\),此时交换\(w\)与\(h\)显然是优的,因为我们在\(maxh\)没有变动的情况下,是的\(sumw\)尽量的小
2.\(w>maxh\),这时候直接交换不一定是优的,因此我们需要计算一下交换之后的面积
如果\(面积_{当前算出来的}
>面积_{以前算出来的}\) 显然不优,我们不去交换\(w\)与\(h\),反之则交换,并注意更新\(maxh\)与一些数组
当\(w < h\)的时候,考虑\(maxh\)与\(h\)的关系
1.\(h\neq
maxh\),显然,我交换之后不影响你\(h\)的最大值,并且交换之后,我\(sumw\)还变大了,面积也会变大,显然不优,我们不去交换
2.\(h = maxh\),还要继续分析:
整体序列是否只有这么一个\(maxh\),那我们需要计算交换之后的面积,然后……(上文提过,此处省略)
如果不止一个\(maxh\),那我们交换之后显然吃亏,因为我们交换之后的\(h\)没有影响到\(maxh\)并且我的\(sumw\)也变大了
算法整体就结束了,不过仍然有一个细节要注意,因为题目规定,要求不超过\(\dfrac n
2\)的修改个数,所以每一次交换之后,都需要累加计数器,如果发现计数器大于\(\dfrac n 2\)就退出
底下是90分代码,还需要经过我再D一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
   | #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <vector> #include <cmath>
  using namespace std;
  const int N = 1e5+66;
  struct node {int h, w, cha;}a[N];
  inline int cmp(node x, node y) {return x.cha > y.cha;}
  int n, m, sum; int sumw[N], maxh = -N;
  int main () {    scanf ("%d", &n);    m = n>>1;    int dodododo = 0;    for (int i = 1; i <= n; ++ i) {       scanf ("%d%d", &a[i].w, &a[i].h);       a[i].cha = abs(a[i].w-a[i].h);       dodododo += a[i].w;       maxh = (maxh>a[i].h)?maxh:a[i].h;    }    sum = dodododo*maxh; #ifdef debug cout << "dodododo-->" << dodododo << ' ' << "maxh-->" << maxh << '\n'; cout << "sum-->" << sum << '\n';  #endif     sort (a+1, a+n+1, cmp); #ifdef debug for (int i = 1; i <= n; ++ i) cout << a[i].w << ' ' << a[i].h << '\n'; #endif    for (int i = 1; i <= n; ++ i) sumw[i] = sumw[i - 1] + a[i].w;    for (int i = 1; i <= n; ++ i) {       if (m == 0) break;       if (a[i].w > a[i].h) {          if (a[i].w <= maxh) {             sum = maxh*(sumw[i - 1]+a[i].h+sumw[n]-sumw[i]);             swap(a[i].w, a[i].h);             for (int j = i; j <= n; ++ j) sumw[j] = sumw[j - 1] + a[j].w;             -- m;          } else {             int ans = a[i].w*(sumw[i-1]+a[i].h+sumw[n]-sumw[i]);             if (ans < sum) {                sum = ans;                for (int j = i; j <= n; ++ j) maxh = a[i].w;                -- m; 	    } else continue; 	 }       }       if (a[i].w == a[i].h) {continue;}       if (a[i].w < a[i].h) {          if (maxh != a[i].h) {continue;}          if (maxh == a[i].h) {             int flag = 0;             for (int j = 1; j <= n; ++ j)                if (a[j].h == maxh) ++ flag;             flag -= 1;             if (flag) {continue;}             else {                int one = -N; 	       for (int j = 1; j < i; ++ j) one =(one>a[j].h)?one:a[j].h; 	       for (int j = i+1; j <= n; ++ j) one =(one>a[j].h)?one:a[j].h; 	       one = (one>a[i].w)?one:a[i].w; 	       int ans = one*(sumw[i-1]+a[i].h+sumw[n]-sumw[i]); 	       if (ans < sum) {                   sum = ans;                   maxh = one;                   swap(a[i].w, a[i].h);                   for (int j = i; j <= n; ++ j)sumw[j]=sumw[j - 1] + a[i].w;                } else {continue;}             }          }       }    }    cout << sum;    return 0; }
   | 
 
第三题
eat
一眼枚举
关于吃东西这道题,我们首先来分析一下题意
给你\(ABCD\)四堆数,告诉每堆数里面你必须选一个,再给你一个判定器n,问你有多少种方案选数?
最暴力的做法,四重for,可以拿到三十分
然后考虑优化,最简单的优化是知道前三个数,然后利用判定器减去前三数的和就可以得到第四个数
在这个题中,我们如果知道了\(A+B\)的和,就可以很容易的推出\(C+D\)的具体范围,
具体做法:枚举出\(A+B\)的所有组合,再枚举\(C+D\)的所有组合,
我们定义\(C1,C2\)数组
其中\(C1[i]=x\)就表示\(A+B\)所能凑出来的和的第\(i\)种方案,其价值为\(x\),同理
\(C2[i] = x\)表示\(C+D\)所能凑出来的和的第\(i\)种方案,其价值为\(x\)
在\(C2\)数组中找到能与\(C1\)数组匹配的数的下标,然后就可以累加答案
更具体的分析在代码中
下面是自己的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
   | #include <bits/stdc++.h> #define int long long  #define LL long long #define debug using namespace std;
  const int N = 1e8+66, M = 5e3+66, P = 2e7+2;
  int n, cnt1, cnt2, maxn, res; int A, B, C, D; int a[M], b[M], c[M], d[M]; int c1[P], c2[N], f[N]; inline int thestars() {    cin >> n >> A >> B >> C >> D;    for (int i = 1; i <= A; ++ i) cin >> a[i];    for (int i = 1; i <= B; ++ i) cin >> b[i];    for (int i = 1; i <= C; ++ i) cin >> c[i];    for (int i = 1; i <= D; ++ i) cin >> d[i];    for (int i = 1; i <= A; ++ i) {       for (int j = 1; j <= B; ++ j) {          if (a[i] + b[j] <= n) {             ++ f[a[i] + b[j]]; 	    maxn = max (maxn, a[i] + b[j]); 	 }       }    }    for (int i = 0; i <= maxn; ++ i) {       while (f[i]) {          -- f[i];          c1[++ cnt1] = i;       }    }    maxn = 0;    for (int i = 1; i <= C; ++ i) {       for (int j = 1; j <= D; ++ j) {          if (c[i] + d[j] <= n) {          ++ f[c[i] + d[j]];          maxn = max (maxn, c[i] + d[j]);          }       }    }    for (int i = 0; i <= maxn; ++ i) {       while (f[i]) {          -- f[i];          c2[++ cnt2] = i;       }    }    int cur;    for (cur = cnt2; cur >= 1; -- cur)       if (c1[1] + c2[cur] <= n)          break;                for (int i = 1; i <= cnt1; ++ i) {       res += cur;
        while (cur && c1[i + 1] + c2[cur] > n)          -- cur;
     }    cout << res << '\n';    return 0; }
  int youngore = thestars();
  signed main() {;}
   |