2020牛客寒假算法基础集训营6——ADFGJ简要题解

竞赛传送门:https://ac.nowcoder.com/acm/contest/3007

A.配对(贪心+思维)

在这里插入图片描述

题目分析及AC代码:

题解:
作者:珩月
链接:https://ac.nowcoder.com/discuss/367149?type=101&order=0&pos=9&page=2
来源:牛客网

我们要使得第K大的和尽可能大,显然可以贪心:
首先,组成这K对数字的显然是A中最大的K个数字和B中最大的K个数字。
问题转化为怎样配对使得最小的和最大:
我们发现,如果A1<A2,B1<B2,那么一定是由A1和B2配对较优。
经过简单的归纳可以得到,倒序配对是最优的,这样就解决了问题。


#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
bool cmp(int x,int y)
{
	return x > y;
}
int main()
{
	ll n=0,k=0;
	cin>>n>>k;
	static ll a[100010]={0};
	static ll b[100010]={0};
	static ll c[100010]={0};
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&b[i]);
	}
	sort(a+1,a+n+1,cmp);
	sort(b+1,b+n+1,cmp);
	for(ll i=1,j=k;i<=k;i++,j--)
	{
		c[i]=a[i]+b[j];
	}
	sort(c+1,c+k+1);
	cout<<c[1]<<endl;
	return 0;
} 

D.重拍列(思维)

在这里插入图片描述

题目分析及AC代码:

先将两个数组从小到大排序,然后观察A数组中有几个数字小于等于B数组的第i位,然后将这个数量记录下来,然后每一位再减去i-1就行了。以样例为例子,B数组为:1 2 3 4,其中A数组中小于等于1的有2个数字,小于等于2的有3个数字,小于等于3的有4个数字,小于等于4的有4个数字。也就意味着,第一个位置有2种情况可供选择,第二个位置有3-1=2种(因为第一个位置选了一个),第三个位置有4-1-1=2种(前两个位置各选了一个),以此类推……然后将每一个位置的可能的数量依次相乘即为最终答案。


#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
bool cmp(int x,int y)
{
	return x > y;
}
int main()
{
	ll n=0,ans=1;
	cin>>n;
	static ll a[100010]={0};
	static ll b[100010]={0};
	static ll c[100010]={0};
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
	scanf("%lld",&b[i]);
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	for(ll i=1,j=1;i<=n;i++)
	{
        while(j<=n&&a[j]<=b[i])
        j++;
        c[i]=j-1;
        //j=1;
    }
	for(ll i=1;i<=n;i++)
	c[i]=c[i]-i+1;
	for(ll i=1;i<=n;i++)
	ans=((ans%mod)*(c[i]%mod))%mod;
	cout<<ans%mod<<endl;
	return 0;
} 

F.十字阵列(思维巧妙降低时间复杂度)

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

题目分析及AC代码:

我们可以先将矩阵里每一个位置的权值算出来,每一行和每一列的权值是有规律的,然后在统计伤害的时候找出对应坐标的权值之和再减去(xi+yi)(因为这个点被算了两次),然后再乘上相应的伤害值zi就行,注意开longlong。

#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
int main()
{
	static ll hang[2010]={0};
	static ll lie[2010]={0};
	ll n=0,m=0,h=0;
	ll ans=0;
	cin>>n>>m>>h;
	hang[1]=(2+m+1)*m/2;
	for(ll i=2;i<=n;i++)
	hang[i]=hang[i-1]+m;
	lie[1]=(2+n+1)*n/2;
	for(ll i=2;i<=n;i++)
	lie[i]=lie[i-1]+n;
	while(h--)
	{
		ll xi,yi,zi;
		scanf("%lld%lld%lld",&xi,&yi,&zi);
		ans+=(((hang[xi]%mod+lie[yi]%mod-xi%mod-yi%mod)%mod)*(zi%mod))%mod;
	}
	cout<<ans%mod<<endl;
	return 0;
} 

G.括号序列(数据结构之栈的应用)

在这里插入图片描述

题目分析及AC代码:

遇到左括号压入堆栈,遇到右括号如果栈顶是左括号就弹出,否则也入栈,最后看看栈是否为空,如果是的话输出0,不是的话输出栈内元素的个数。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
string a;
char s[1000010];
int main()
{
    int n=0;
    cin>>n;
    while(n--)
    {
        int N=0;
        cin>>N;
        cin>>a;
    getchar();
    int flag=1;
    int j=0;
    for(int i=0;i<N;i++)
    {
        if(a[i]=='(')
        {
            s[j]=a[i];
            j++;
            continue;
        }
        else
        {
            if(a[i]==')'&&s[j-1]=='(')
            {
                j--;
                continue;
            }
            else
            {
                s[j]=a[i];
                j++;
            }
        }
    }
    if(j!=0)
    cout<<j<<endl;
    else
    cout<<0<<endl;
    }
    return 0;
}

J.签到题(数学+几何)

在这里插入图片描述

题目分析及AC代码:

首先我们不妨设三条边长分别为a,b,c,首先判断能否形成三角形(任意两边之和是否大于第三边),如果能形成三角形的话,我们不妨假设三个圆的半径分别为r1,r2,r3。如果三个圆两两相切的话,应该是a=r1+r3,b=r2+r3,c=r1+r2。现在a,b,c已知,可以通过消元法解出r1,r2,r3,如果三者均合法存在(大于0),则输出“Yes”,并输出长度,否则输出“No”。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
    double a=0,b=0,c=0;
    double r1=0,r2=0,r3=0;
    cin>>a>>b>>c;
    if((a+b<=c)||(a+c<=b)||(b+c<=a))
    {
        cout<<"wtnl"<<endl;
        return 0;
    }
    r2=(c+b-a)/2;
    r1=c-r2;
    r3=a-r1;
    double x[5]={0};
    x[0]=r1;
    x[1]=r2;
    x[2]=r3;
    sort(x,x+3);
    if(r1>0&&r2>0&&r3>0)
    {
        cout<<"Yes"<<endl;
        printf("%.2lf %.2lf %.2lf",x[0],x[1],x[2]);
        cout<<endl;
    }
    else
    cout<<"No"<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值