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

总结:
这次做的不好,爆零了,虽然确实本场题比较难,但是没做出来确实问题很大,考虑问题不够全面仔细。

I

题意

给出n对数字a,b。有三种操作。第一种,什么也不做。第二种,如果ai在在前面没有被选过,可以选择ai。第三种,如果bi在前面没有被选过,可以选择bi。
求最多可以选择多少种不同的数字。

思路

对数字进行离散化,若两数分在一组,中间连一条边。对于每条边只能选其中一个端点。如果不成环,则有一个点不会被选到。若成环,连通图中的每一个点都可以被选择。用vis数组判断是否成环。

代码

#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false),cin.tie(0);
#define ll long long
#define inf 0x3f3f3f3f
const int N=1e5+5;
//set<string>b;
//set<string>::iterator it;
const int M=2e5+10;
int a[N],b[N],c[M],vis[M],f[M];
int find(int p)
{
	if(f[p]==p) return p;
	return f[p]=find(f[p]);
}
void merge(int x,int y)
{
	int tx=find(x),ty=find(y);
	if(tx==ty)
	{
		vis[tx]=true;return;
	}
	f[tx]=ty;
	if(vis[tx]) vis[ty]=true;
}
int main()
{
    IO;
    int T,n,i,j,len,ans,ca=0;
    cin>>T;
    while(T--)
    {
    	memset(vis,false,sizeof(vis));
    	for(i=0;i<M;i++) f[i]=i;
		cin>>n;
    	j=0;
    	for(i=0;i<n;i++)
    	{
    		cin>>a[i]>>b[i];
    		c[j++]=a[i];
    		c[j++]=b[i];
		}
		len=j;
		sort(c,c+len);
		len=unique(c,c+len)-c;
		for(i=0;i<n;i++)
		{
			int t1=lower_bound(c,c+len,a[i])-c;
			int t2=lower_bound(c,c+len,b[i])-c;
			merge(t1,t2);
		}
		ans=len;
		for(i=0;i<len;i++)
		{
			if(!vis[i]&&f[i]==i) ans--;
		}
		cout<<"Case #"<<++ca<<": "<<ans<<endl;
	}
    return 0; 
}

K

题意

给出每种菜品的利润以及碟数,要求我们给每个客人至少一碟菜,要求从1号菜品开始给,给的菜品的号码是连续的,每个客人同号码的菜都只能给一碟。求能招待客人的最大数量以及在客人最多的情况下能获得的最大利润。

思路

解题思路:

1.首先,因为要求从1号菜品开始给,所以能招待的客人的最大数量就是1号菜品的碟数。

2.用结构体记录1-x号菜品各一碟可以获得的利润以及x的位置,后续按照利润从大到小的顺序对结构体进行排序。

3.给客人的菜一定是1-x各一盘这种情况,可以用一个time数组对能够上几次1-x的菜进行计数,time[x]即1-x中b[x]的最小值。

4.对结构体排序之后进行遍历,每次用最大的利润乘以相应的time数组值,再模拟该操作对其他菜品的影响,多次相加之后得到答案。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct noid{
    int loc;//x的值
    long long sum;//1-x菜各一碟利润总和
}e[100005];
bool cmp(struct noid a,struct noid b)
{
    return a.sum>b.sum;
}
//输入
inline __int128 read()
{
   int X=0,w=0; char ch=0;
   while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
   while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
   return w?-X:X;
}
//输出
inline void print(__int128 x)
{
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}
int main()
{
    int i,j,t,n,k,now,nowt;
    __int128 a[100005],b[100005],time[100005],ans;
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    {
        ans=0;
        scanf("%d",&n);
        e[0].sum=0;
        for(j=1;j<=n;j++)
        {
            a[j]=read();
            e[j].sum=e[j-1].sum+a[j];
            e[j].loc=j;
        }
        for(j=1;j<=n;j++)
        {
            b[j]=read();
            if(j==1)
                time[1]=b[1];
            if(j>1)
                time[j]=min(time[j-1],b[j]);//求1-j菜品最小值,即1-j能给几次客人
        }
        sort(e+1,e+1+n,cmp);//对利润进行排序
        nowt=0;now=100005;
        for(j=1;j<=n;j++)
        {
            if(e[j].loc>=now)//因为now前面的菜品一定会出现部分空缺,位置在now后面上不了菜
                continue;
            ans=ans+e[j].sum*(time[e[j].loc]-nowt);//把该顺序菜品全部上完
            now=e[j].loc;//记录目前的位置
            nowt=time[e[j].loc];//记录当前1号菜品被消耗的次数
            if(nowt==b[1])//判断优化,减少时间
                break;
        }
        if(i==t)
        {
            printf("Case #%d: %lld ",i,b[1]);
            print(ans);
        }
        else
        {
            printf("Case #%d: %lld ",i,b[1]);
            print(ans);
            printf("\n");
        }
    }
    return 0;
}

G

题意

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

思路

暴力模拟O(n^3)能过,但是比赛的时候感觉题干很长就放弃了,赛后看到这个题好像是去年上海的一道改编,顺便也看了一下。

代码

#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;
}


待定

题意

思路

代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值