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

I-Interesting Computer Game

链接:https://ac.nowcoder.com/acm/contest/5673/I
题意:
有T组数据,每组数据有n对数每对数只能选择一个数或者不选,且前面没有选过相同的数,最后保证找到的数最多。
思路:
我们将每对数连成一条边,每次只能选边上的一个顶点,n对数连成一个图,图分为以下两种情况

1.连成一棵树,那么n个点n-1条边中我们最多只能选n-1个点,因为每组数据中我们最多只能选一个
2.连成一个环,那么环上所有点都可以选择,包括与环连接的连通图上所有顶点
那么我们就需要一个布尔数组circle来判断这个点是否是在环内,如果遇到两个点的祖先结点相同那么就说明他们在一个环内,如果不相同就将两者合并
代码:

#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <unordered_map>

using namespace std;

typedef long long ll;
const int N = 2e5 + 100;
unordered_map<int,int> mp;
int cnt,f[N];
bool cicle[N];

int Find(int x)//查找祖先结点
{
    if(f[x] == x)
    {
        return x;
    }
    else
    {
        return f[x] = Find(f[x]);
    }
}

void Union(int x, int y)
{
    int xx = Find(x);
    int yy = Find(y);
    if(xx != yy) //如果父结点不相等就合并
    {
        f[xx] = yy;
        cicle[yy] |= cicle[xx];//或等于,将两者的或运算结果赋给cicle[y],只有两者都为树时合并才为树
    }
    else
    {
        cicle[xx] = true;//如果相等证明是一个环,所有顶点都可以算进去
    }
}

void Init()
{
    mp.clear();
    cnt = 0;
    for(int i = 0 ; i < N; i++)
    {
        f[i] = i;
        cicle[i] = false;
    }
}

int main()
{
    int t,Case = 0;
    cin >> t;
    while(t--)
    {
        Init();
        int n;
        scanf("%d",&n);
        for(int i = 1 ; i <= n ; i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            if(!mp[x])
            {
                mp[x] = ++cnt;
            }
            if(!mp[y])
            {
                mp[y] = ++cnt;
            }
            Union(mp[x],mp[y]);
        }
        int ans = cnt;
        for(int i = 1 ; i <= cnt ; i++)
        {
            if(f[i] == i && !cicle[i])//如果遇到一棵树
            {
                ans--;
            }
        }
        printf("Case #%d: %d\n",++Case,ans);
    }
    return 0;
}

K-Kabaleo Lite

链接:https://ac.nowcoder.com/acm/contest/5673/K
题意:
有n种菜,给出两组长度为n的数组,第一组表示每种菜的利润(可能为负),第二组表示每种菜的份数,每次给一个顾客上菜,必须是从第一份开始的连续的菜,问最多可以有给几个顾客上菜(优先保证),最大利润是多少。
思路:
首先求出利润的前缀和,份数只记录min(当前,上一个),对前缀和降序排序,排序时需要记录原始下标排序,遍历前缀和,记录一个右边界,如果当前前缀和的原始下标在右边界的左边,ans累加利润乘份数,更新当前已经用过的份数即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

const int maxn = 1e5+5;
struct node{
    ll val;
    __int128 sum;
    int i;
}a[maxn];
ll b[maxn];

bool cmp(node x,node y)
{
    return x.sum>y.sum;
}

inline void print(__int128 x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9)
        print(x/10);
    putchar(x%10+'0');
}

int main()
{
    int t,cas=0;
    scanf("%d",&t);
    while(t--){
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i].val);
            a[i].sum=a[i].val+a[i-1].sum;
            a[i].i=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%lld",&b[i]);
            if(i!=1) b[i]=min(b[i],b[i-1]);
        }
        sort(a+1,a+1+n,cmp);
        int x=n;
        ll cnt=0;//记录已经消耗的数量 
        __int128 ans=0;
        for(int i=1;i<=n;i++){
            if(x>=a[i].i) ans+=a[i].sum*(b[a[i].i]-cnt),cnt=b[a[i].i],x=a[i].i;
        }
        printf("Case #%d: %lld ",++cas,b[1]);
        print(ans);
        puts("");
    }
    return 0;
 }

G-Game SET

链接:https://ac.nowcoder.com/acm/contest/5673/G
题意:
一套牌有四种属性,每种属性都有三种特征,shapes(one,two,orthree)shape(diamond,squiggle,oroval)shading(solid,striped,oropen), color(red,green,orpurple),如果是 ∗,可以选任意一种。给出 n 套牌,每套牌给出 [<number>][<shape>][<shading>][<color>],问有没有三张牌符合同一属性的特征要么全都相同,要么全都不同。
思路:
先用 map 将字符串转换为数字记录下每张牌的四种属性,然后三个循环找三张牌,遍历属性,如果全都符合条件就输出三张牌的编号,如果没有符合条件的就输出 −1。
如果遇到了 ∗,如果另外两张相同,那么 ∗ 可以和它们相同,否则和它们都不同,所以该属性只要有一个 ∗ 符合条件。
如果没有∗ ,就要么全都相同,要么全都不同符合条件。
代码</color></shading></shape></number>

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1010;
char s[N][110];
int cas = 1;
bool solve(string a, string b, string c)
{
    if (a == "*" || b == "*" || c == "*")
        return true;
    if (a == b && a != c)
        return false;
    if (a == c && a != b)
        return false;
    if (b == c && a != b)
        return false;
    return true;
}
bool check(int i, int j, int k)
{
    int cnt1, cnt2, cnt3;
    int num = 0;
    cnt1 = cnt2 = cnt3 = 0;
    while (num < 4)
    {
        num++;
        cnt1 += 2, cnt2 += 2, cnt3 += 2;
        string s1, s2, s3;
        while (s[i][cnt1] != ']')
            s1 += s[i][cnt1], cnt1++;
        while (s[j][cnt2] != ']')
            s2 += s[j][cnt2], cnt2++;
        while (s[k][cnt3] != ']')
            s3 += s[k][cnt3], cnt3++;
        if (!solve(s1, s2, s3))
            return false;
    }
    return true;
}
int main()
{
    int T;
    sd(T);
    while (T--)
    {
        int n;
        sd(n);
        rep(i, 1, n)
            ss(s[i] + 1);
        int flag = 0;
        rep(i, 1, min(n, 21))
        {
            rep(j, i + 1, min(21, n))
            {
                rep(k, j + 1, min(21, n))
                {
                    if (check(i, j, k))
                    {
                        flag = 1;
                        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;
}
全部评论

相关推荐

05-09 13:22
门头沟学院 Java
点赞 评论 收藏
分享
牛客928043833号:在他心里你已经是他的员工了
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务