竞赛传送门: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;
}