2021牛客暑期多校训练营1

126 篇文章 0 订阅
32 篇文章 0 订阅


A–Alice and Bob

题目链接:


题意:

总共有两堆石头,每次可以从一堆中取走 k k k ( k > 0 ) (k>0) (k>0)块石头,从另一堆取走 s ∗ k s*k sk ( s > = 0 ) (s>=0) (s>=0)块石头, 不能操作的人输


题解:

用一个二维数组 f [ i ] [ j ] f[i][j] f[i][j] 储存状态, f [ i ] [ j ] f[i][j] f[i][j] 1 1 1 代表有两堆石头分别有 i , j i,j i,j 块的时候先手必胜,为 0 0 0 代表先手必败

可以发现一个状态 ( i , j ) (i,j) (i,j) 如果为先手必败态, 则上一个状态 ( i + k , j + s ∗ k ) (i+k,j+s*k) (i+k,j+sk) 一定为先手必胜态, 则对于每一个必败状态,枚举寻找 k , s k,s k,s

码来!

#include <bits/stdc++.h>
using namespace std;
const int N=5e3+10;
bool f[N][N];
void init()
{
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(f[i][j]==0)
            {
                for(int k=1;k+i<N;k++)
                {
                    for(int s=0;s*k+j<N;s++)
                    {
                        int xx=k+i;
                        int yy=s*k+j;
                        if(xx<yy) swap(xx,yy);
                        f[xx][yy]=1;
                    }
                }
                for(int k=1;k+j<N;k++)
                {
                    for(int s=0;s*k+i<N;s++)
                    {
                        int xx=k+j;
                        int yy=s*k+i;
                        if(xx<yy) swap(xx,yy);
                        f[xx][yy]=1;
                    }
                }
            }
        }
    }
}
int main()
{
    init();
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        if(n<m) swap(n,m);
        if(f[n][m]) cout<<"Alice"<<endl;
        else cout<<"Bob"<<endl;
    }
 
    return 0;
}

B–Ball Dropping

题目链接:


题意:

一个球体在等腰梯形中,落下请确定球是否会卡在梯形中。如果答案是“卡住”,请同时计算卡住位置(球心与底面中点之间的高度)。


题解:

在这里插入图片描述
如图:

可知,绿色三角形的斜边可以计算, 同时绿色三角形与黄色三角形相似, 则黄色三角形的三边可以计算得到

得到黄色三角形的斜边 y y y ,已知 z z z 部分为 b / 2 b/2 b/2, 则可计算 x x x部分的值

则以 x x x边和粉色边组成的三角形又与绿色三角形相似,可得到粉色边的值,即为答案

码来!

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int r,a,b,h;
    cin>>r>>a>>b>>h;
    if(2*r<b) cout<<"Drop"<<endl;
    else
    {
        double s=sqrt(h*h+((a-b)/2.0)*((a-b)/2.0));
        double y=r*s/h;
        double x=y-b/2.0;
        double ans=2.0*h*x/(a-b)*1.0;
        cout<<"Stuck"<<endl;
        printf("%.8f\n",ans);
    }
    return 0;
    
}

D–Determine the Photo Position

题目链接:


题意:

将连续 m m m 2 2 2 插入到矩阵中 0 0 0 的位置,有多少种方法


题解:

计算每行中连续的 0 0 0 的个数即可

码来!

#include <bits/stdc++.h>
using namespace std;
char a[2005][2005];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	int ans=0;
	string b;
	cin>>b;
	for(int i=0;i<n;i++)
	{
		int c=0;
		for(int j=0;j<n;j++)
		{
			if(a[i][j]=='1')
			{
				ans+=max(0,c-m+1);
				c=0;
			}
			else c++;
		}
		if(c) ans+=max(0,c-m+1);
	}
	cout<<ans<<endl;
	return 0;
}

F–Find 3-friendly Integers

题目链接:


题意:

计算区间内有多少个数满足其中含有一段连续子串是 3 3 3的倍数


题解:

可以发现大于 100 100 100 的数都满足这个条件,于是先枚举 100 100 100 以内的满足条件的数字,然后计算区间内的个数

#include <bits/stdc++.h>
using namespace std;
int f[105];
int main()
{
    int con=0;
    for(int i=1;i<=100;i++)
    {
        if(i%3==0)
        {
            con++;
            f[i]=con;
            continue;
        }
        int j=i;
        while(j)
        {
            int d=j%10;
            if(d==0||d==3||d==6||d==9)
            {
                con++;
                break;
            }
            j/=10;
        }
        f[i]=con;
    }
    int t;
    cin>>t;
    while(t--)
    {
        long long n,m;
        cin>>n>>m;
        long long ans1,ans2;
         
        if(m<=100) ans2=f[m];
        else  ans2=76+(m-100);
         
        if(n-1<=100) ans1=f[n-1];
        else ans1=76+(n-100-1);
         
        cout<<ans2-ans1<<endl;
    }
    return 0;
}

G–Game of Swapping Numbers

题目链接:


题意:

A A A 序列交换 k k k 次,使得 ∑ i = 1 n ∣ A i − B i ∣ \sum_{i=1}^n{\vert A_i-B_i \vert} i=1nAiBi最大


题解:

首先考虑怎样改变可以使答案变大

对于每一个 ∣ A i − B i ∣ {\vert A_i-B_i \vert} AiBi , 去掉绝对值符号后一定有一个数字前面为负号, 一个为正号, 所以答案便是这些数字的和, 为了使答案变大, 尽量使小的数前面是负号, 大的数字前面为正号

n = 2 n=2 n=2 的时候,特判

n > 2 n>2 n>2 的时候, A A A 序列必定有至少两个数字前面符号都为 + + + − - , 假设这两个数字分别为 A i A_i Ai A j A_j Aj并且前面符号都为 − - , 则对于这两个数字和 B B B 序列对应数字的排列有两种情况

( 1 ) (1) (1) 相交

在这里插入图片描述
可以发现对于这种情况,交换 A i A_i Ai A j A_j Aj,结果并不会增加

( 2 ) (2) (2) 相离

在这里插入图片描述
可以发现交换 A i A_i Ai A j A_j Aj,后将会使结果增加两倍的短的红色的线的距离

即当 n > 2 n>2 n>2 的时候可以保证不会使答案结果变小,对于第二种相离的情况,可以表达为

如果 m i n ( A i , B i ) > m a x ( A j , B j ) min(A_i,B_i)>max(A_j,B_j) min(Ai,Bi)>max(Aj,Bj)

a n s + = 2 ∗ ( m i n ( A i , B i ) − m a x ( A j , B j ) ) ans+=2*(min(A_i,B_i)-max(A_j,B_j)) ans+=2(min(Ai,Bi)max(Aj,Bj))

所以首先将所有的 m i n ( A i , B i ) min(A_i,B_i) min(Ai,Bi) 按照从大到小排序, 将 m a x ( A i , B i ) max(A_i,B_i) max(Ai,Bi) 按照从小到大排序, 一旦 m i n ( A i , B i ) < m a x ( A j , B j ) min(A_i,B_i)<max(A_j,B_j) min(Ai,Bi)<max(Aj,Bj) 此时交换则会将答案变小,所以此时停止交换,剩下的交换次数可以通过相交的情况使用掉

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
long long a[N],b[N],ma[N],mi[N];
int main()
{
	long long n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		ma[i]=max(a[i],b[i]);
		mi[i]=min(a[i],b[i]);
		ans+=ma[i]-mi[i];
	}
	if(n==2)
	{
		if(k%2==0) cout<<ans<<endl;
		else
		{
			ans=abs(a[1]-b[2])+abs(a[2]-b[1]);
			cout<<ans<<endl;
		}
	}
	else
	{
		sort(ma+1,ma+1+n);
		sort(mi+1,mi+1+n,greater<long long>());
		for(int i=1;i<=k&&i<=n;i++)
		{
			if(mi[i]>=ma[i]) 
			{
				ans+=2*(mi[i]-ma[i]);
			}
			else break;
		}
		cout<<ans<<endl;
	}
	return 0;
}

K–Knowledge Test about Match

题目链接:


题意:

给定两个序列

a a a = = = 0 , 1 , 2 , . . . . . . , n {0,1,2,......,n} 0,1,2,......,n

b b b = = = b 0 , b 1 , b 2 . . . . . . , b n − 1 b_0,b_1,b_2......,b_n-1 b0,b1,b2......,bn1

函数

f ( a , b ) = f(a,b) = f(a,b)= ∑ i = 0 n − 1 ∣ a i − b i ∣ \sum_{i=0}^{n-1} {\sqrt{\vert a_i-b_i \vert}} i=0n1aibi

T T T 组测试数据,设第 k k k 组测试的标准答案的loss函数为 f k ∗ f{_k^*} fk, 你的输出结果的loss函数为 f k ^ \hat{f_k} fk^ , 则只需满足以下不等式即可:

在这里插入图片描述


题解:

x − 1 x^ {-1} x1 图像

在这里插入图片描述

可以知道 a i a_i ai b i b_i bi 越接近, 答案越接近标准答案, 于是使用贪心策略,

#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int cnt[N],ans[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(cnt,0,sizeof cnt);
        memset(ans,-1,sizeof ans);
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            int x;
            cin>>x;
            cnt[x]++;
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(cnt[j])
                {
                    if(cnt[j]&&i+j<n&&ans[i+j]==-1)
                    {
                        cnt[j]--;
                        ans[i+j]=j;
                    }
                    if(cnt[j]&&j-i>=0&&ans[j-i]==-1)
                    {
                        cnt[j]--;
                        ans[j-i]=j;
                    }
                }
            }
        }
        for(int i=0;i<n;i++) cout<<ans[i]<<" ";
        cout<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值