2020牛客暑期多校训练营(第八场)解题报告

2020牛客暑期多校训练营(第八场)

第八场了,终于进了一次前500,贴个榜单纪念一下
在这里插入图片描述

G. Game SET

题目大意

定义一张牌有四个属性,分别为大小,形状,阴影和颜色,一个集合包含三张牌,并且每个集合中对于所有牌的某种属性,要么完全相同要么完全不同,问你在给定的 n n n张牌里面有没有满足条件的三张牌能够构成一个集合,若某张牌的某一属性为‘*’,说明该属性为任意值(俗称赖子)

解题思路

这题应该是这场真正的“签到题”,直接 O ( n 3 ) O(n^3) O(n3)的暴力就过了,不知道谁把榜给带歪了。。。

主要的做法就是从给定的 n n n张牌里面任选三张,暴力判断是否满足条件即可

AC代码
#include <bits/stdc++.h>
using namespace std;
string s[300][5];
bool judge(int i, int j, int k) {
    bool flag = true;
    for(int p = 1; p <= 4; ++p) {
        if(s[i][p] == s[j][p] && s[j][p] == s[k][p]) continue;
        if(s[i][p] != s[j][p] && s[i][p] != s[k][p] && s[j][p] != s[k][p]) continue;
        if(s[i][p] == "*" && s[j][p] == s[k][p]) continue;
        if(s[j][p] == "*" && s[i][p] == s[k][p]) continue;
        if(s[k][p] == "*" && s[i][p] == s[j][p]) continue;
        if(s[i][p] == "*" && s[j][p] == "*") continue;
        if(s[j][p] == "*" && s[k][p] == "*") continue;
        if(s[i][p] == "*" && s[k][p] == "*") continue;
        flag = false;
        break;
    }
    return flag;
}
int main() {
    int T;
    scanf("%d", &T);
    int cas = 0;
    while(T--) {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            string p;
            cin >> p;
            int len = p.length();
            int cnt = 0, pre = 0;
            for(int j = 0; j < len; ++j) {
                if(p[j] == '[') pre = j;
                else if(p[j] == ']') {
                    s[i][++cnt] = p.substr(pre + 1, j - pre - 1);
                }
            }
            //cout << s[i][1] << " " << s[i][2] << " " << s[i][3] << " " << s[i][4] << '\n';
        }
        bool flag = false;
        for(int i = 1; i <= n; ++i) {
            for(int j = i + 1; j <= n; ++j) {
                for(int k = j + 1; k <= n; ++k) {
                    if(judge(i, j, k)) {
                        flag = true;
                        printf("Case #%d: %d %d %d\n", ++cas, i, j, k);
                        break;
                    }
                }
                if(flag) break;
            }
            if(flag) break;
        }
        if(!flag) printf("Case #%d: -1\n", ++cas);
    }
    return 0;
}

K. Kabaleo Lite

题目大意

一家餐馆有 n n n种菜,每道菜有 a i a_{i} ai盘,每盘的收益为 b i b_{i} bi,每个客人都必须从第一盘菜开始点,并且其点菜顺序为 1 , 2 , 3... n 1,2,3...n 1,2,3...n,即点菜顺序必须连续,每道菜每个客人只能点一盘,问你在满足客人数最大的情况下所获得的的最大收益是多少

解题思路

首先考虑客人数最多,那肯定就是第一道菜的盘数,因为第一道菜点完后后面的菜就点不了了

其次考虑收益,由于点菜顺序必须连续,那么我肯定能点就点,也就是说我们将菜的收益的前缀和进行排序,按照前缀和的大小进行点菜

另外,假设第 i i i中菜已经点完了,那么 i i i以后的菜就不能点了,所以 a i = m i n ( a 1 , a 2 , . . . , a i ) a_{i}=min(a_{1},a_{2},...,a_{i}) ai=min(a1,a2,...,ai),按照前缀和排序后要注意菜被点完的情况

注:这题最大收益会把 l o n g   l o n g long\ long long long爆掉,并且由于有负数的原因 u n s i g n e d   l o n g   l o n g unsigned\ long\ long unsigned long long不能用,只好用 _ _ i n t 128 \_\_int128 __int128代替了

AC代码
#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <string>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <set>
#include <math.h>
#include <stack>
#include <algorithm>
#include <queue>
#include <functional>
#include <ctime>
#include <list>
#include <cstring>
#define mod 1000000007
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false),cout.tie(0)
using namespace std;
#define PI 3.1415926535898
using namespace std;
 
const int maxx = 1e5 + 5;
 
//int profit[maxx];
int dishs[maxx];
inline __int128 read() {
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch<'0' || ch>'9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
 
inline void print(__int128 x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
struct node {
    int id = 0;
    __int128 pro = 0;
}no[maxx];
bool cmp(node a, node b) {
    return a.pro < b.pro;
}
int main() {
    int T;
    scanf("%d", &T);
    for (int i = 1; i <= T; ++i) {
        int N;
        __int128 res = 0;
        scanf("%d", &N);
        //int cnt = 0;
        for (int j = 1; j <= N; ++j) {
            no[j].id = j;
            no[j].pro = no[j - 1].pro + read();
 
        }
        sort(no + 1, no + N + 1, cmp);
        int minn = INF;
        for (int j = 1; j <= N; ++j) {
            scanf("%d", &dishs[j]);
            if (dishs[j] > minn) {
                dishs[j] = minn;
            }
            else {
                minn = dishs[j];
            }
        }
        printf("Case #%d: %d ", i, dishs[1]);
        int minid = INF,cnt = 0;
        for (int j = N; j >= 1 && dishs[1]-cnt > 0; --j) {
            if (no[j].id < minid) {
                res += no[j].pro * (dishs[no[j].id]-cnt);
                minid = no[j].id;
                cnt = dishs[no[j].id];
                //dishs[1] -= dishs[no[j].id];
            }
 
        }
        print(res);
        printf("\n");
        //printf("%lld\n", res);
    }
}

I. Interesting Computer Game

题目大意

n n n次操作,每次你可以从给定的 a i a_{i} ai b i b_{i} bi中选择一个数,问你最后所选数字的种类数最大是多少

解题思路

你可以将 a i a_{i} ai b i b_{i} bi看做两个节点,并且这两个节点之间连了一条边,那么每次操作相当于在一条边上去除一个节点,模拟一下你会发现对于这个图的每个联通子图,如果子图中有环,那么这个子图中所有的节点都可以被选择,否则如果子图是一棵树,那么子图中可选节点数目为总结点数减一

那么这题的做法就是对于每个节点进行 d f s dfs dfs,若经过环,那么该节点所在子图对于答案的贡献为该子图的大小,否则为该子图大小减一

但是由于 a i , b i ≤ 1 e 9 a_{i},b_{i}\leq 1e^{9} ai,bi1e9,我们需要将其离散化到 2 N 2N 2N以内

AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int aaa[maxn];
int bbb[maxn];
int ccc[maxn];
bool vis[maxn];
vector<int> v[maxn];
int res = 0;
bool flag = false;
int dfs(int u, int fa) {
    if (vis[u]) {
        flag = true;
        return 0;
    }
    vis[u] = true;
    int len = v[u].size();
    int res = 1;
    for (int i = 0; i < len; ++i) {
        if(v[u][i] != fa) res += dfs(v[u][i], u);
    }
    return res;
}
int main() {
    int T;
    scanf("%d", &T);
    for (int i = 1; i <= T; ++i) {
        int N;
        scanf("%d", &N);
        int cnt = 0;
        for (int j = 1; j <= N; ++j) {
            scanf("%d%d", &aaa[j], &bbb[j]);
            ccc[++cnt] = aaa[j];
            ccc[++cnt] = bbb[j];
        }
        sort(ccc + 1, ccc + cnt + 1);
        int lenc = unique(ccc + 1, ccc + cnt + 1) - ccc - 1;
        for (int j = 1; j <= lenc; ++j) {
            v[j].clear();
            vis[j] = false;
        }
        for (int j = 1; j <= N; ++j) {
            aaa[j] = lower_bound(ccc + 1, ccc + lenc + 1, aaa[j]) - ccc;
            bbb[j] = lower_bound(ccc + 1, ccc + lenc + 1, bbb[j]) - ccc;
            v[aaa[j]].push_back(bbb[j]);
            v[bbb[j]].push_back(aaa[j]);
        }
        res = 0;
        for (int j = 1; j <= lenc; ++j) {
            if (!vis[j] && v[j].size()) {
                flag = false;
                int size = dfs(j, 0);
                res += flag ? size : size - 1;
            }
        }
        printf("Case #%d: %d\n", i, res);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值