8.1集训

上午

考试

第一题

一句话题意:给你几堆火,给你几个罐子,这几个罐子分别对火可能会产生不同的影响,每次使用罐子的时候都必须对全部的火进行操作,(1)表示浇灭,(0)表示
无影响,(-1) 表示点燃

问最少用几次罐子可以把火浇灭,否则输出“-1”

一眼搜索

#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 eq maxh),显然,我交换之后不影响你(h)的最大值,并且交换之后,我(sumw)还变大了,面积也会变大,显然不优,我们不去交换

2.(h = maxh),还要继续分析:

整体序列是否只有这么一个(maxh),那我们需要计算交换之后的面积,然后……(上文提过,此处省略)

如果不止一个(maxh),那我们交换之后显然吃亏,因为我们交换之后的(h)没有影响到(maxh)并且我的(sumw)也变大了

算法整体就结束了,不过仍然有一个细节要注意,因为题目规定,要求不超过(dfrac n 2)的修改个数,所以每一次交换之后,都需要累加计数器,如果发现计数器大于(dfrac n 2)就退出
底下是90分代码,还需要经过我再D一下

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
//#define debug
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 << '
';
cout << "sum-->" << sum << '
'; 
#endif 
   sort (a+1, a+n+1, cmp);
#ifdef debug
for (int i = 1; i <= n; ++ i) cout << a[i].w << ' ' << a[i].h << '
';
#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)数组匹配的数的下标,然后就可以累加答案

更具体的分析在代码中

下面是自己的代码:

#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;
   //我们在C2数组中寻找最后一个能与C1[1]相匹配的数的下标
   //何为匹配?我加上你可以在判定器的范围之内
   //所以应该倒序枚举,保证前边的一定可以匹配
   for (int i = 1; i <= cnt1; ++ i) {
      res += cur;
//cur表示的是在C2数组中最后一个能与当前的C1[i]匹配的下标
      while (cur && c1[i + 1] + c2[cur] > n)
         -- cur;
//cur这个计数器需要不断往前更新,一直在找,能与C1数组中匹配的下标
   }
   cout << res << '
';
   return 0;
}

int youngore = thestars();

signed main() {;}
原文地址:https://www.cnblogs.com/yszhyhm/p/13413939.html