A. Make it Beautiful
题意
题目链接
给定一个长度为
n
n
n的数组,将该数组重新排列使得数组内的每一个数都不等于其前面的所有数字之和。
思路
首先想到的应该是降序排列,因为所有数都是大于 0 0 0的,但是因为存在重复的数,比如 3366 3366 3366这组样例,最大的数数量超过一个,就导致第二个数不满足要求。那么可以拿一个不等于这大数的其他数放在数组最前面,这样就可以避免这种情况了。否则如果这个数列中所有数都相同,就拿不出来不同的数放前面,就构造不出来。
注意这个题不能关输出同步流,否则 p u t s puts puts函数输出的结果就无法与 c o u t cout cout函数输出的结果同步。当输出函数只有 c o u t cout cout时才可以关输出同步流。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 50 + 5;
ll a[maxn];
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0),cout.tie(0);
ll t;
cin>>t;
while(t--){
ll n;
cin>>n;
for(ll i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n,greater<ll>());
bool flag=false;
for(ll i=2;i<=n;i++){
if(a[i]!=a[1]){
flag=true;
break;
}
}
if(flag==false)puts("NO");
else{
puts("YES");
if(a[1]==a[2]){
ll x,pos;
for(ll i=3;i<=n;i++){
if(a[i]!=a[1]){
x=a[i];
pos=i;
break;
}
}
cout<<x<<" ";
for(ll i=1;i<=n;i++){
if(pos==i)continue;
cout<<a[i]<<" ";
}
cout<<endl;
}
else{
for(ll i=1;i<=n;i++)cout<<a[i]<<" ";
cout<<endl;
}
}
}
return 0;
}
B. Matrix of Differences
题意
题目链接
请构造一个长宽为
n
n
n的矩阵,要求矩阵内所有的数字是
[
1
,
n
2
]
[1,n^2]
[1,n2]的排列,且要求矩阵内每两个相邻数之间的绝对值之差的种类数量最多,输出该矩阵。
思路
将 [ 1 , n 2 ] [1,n^2] [1,n2]按照一当前极小一当前极大的顺序交替蛇形摆放。这其实是一种极值减幅振荡,差值趋近于零的思想。因为只有这么构造,才能给出种类数为 n 2 − 1 n^2-1 n2−1的最大种类数矩阵。按照蛇形差值的方式,横向有 n ( n − 1 ) = n 2 − n n(n-1)=n^2-n n(n−1)=n2−n对差值,纵向有 n − 1 n-1 n−1对差值,一共有 n 2 − n + n − 1 = n 2 − 1 n^2-n+n-1=n^2-1 n2−n+n−1=n2−1对差值,刚好构造出最大种类数矩阵。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 50 + 5;
ll a[maxn][maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll t;
cin>>t;
while(t--){
ll n;
cin>>n;
ll curx = 1, cury = 1;
ll first = 1, last = n*n;
ll row = 1;
for(ll i=1;i<=n*n;i++){
if(i&1)a[curx][cury]=first,first++;
else a[curx][cury]=last,last--;
if(row&1){
cury++;
if(cury==n+1)cury=n,row++;
}
else{
cury--;
if(cury==0)cury=1,row++;
}
if(i%n==0)curx++;
}
for(ll i=1;i<=n;i++){
for(ll j=1;j<=n;j++){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}
return 0;
}
C. Yet Another Tournament
题意
题目链接
有
n
n
n名玩家,战斗力分别为
[
1
,
n
]
[1,n]
[1,n],我们也是一名玩家,一共有
n
+
1
n+1
n+1名玩家。对于
n
n
n名玩家来说,
i
i
i能击杀
j
j
j当且仅当
i
>
j
i>j
i>j。对于每一位玩家我们都有一个准备值
a
i
a_{i}
ai,我们想要击败玩家
i
i
i需要花费
a
i
a_{i}
ai的时间去准备。刚开始我们拥有的时间为
m
m
m,每名玩家都和其余所有玩家进行一轮对局,最终按照胜场数量对所有玩家进行排名,求我们合理分配时间会获得的最高排名。
思路
已知对于玩家 [ 1 , n ] [1,n] [1,n],他们的基础胜场是 [ 0 , n − 1 ] [0,n-1] [0,n−1],假如我们的胜场是 x x x,那么我们就至少能够击败或打平前 x x x名玩家,因为第 x x x名玩家的基础胜场只有 x − 1 x-1 x−1,就算把我们击败了,他的胜场也只能和我们相同。然而,对于第 x + 1 x+1 x+1名玩家,他的基础胜场是 x x x,如果他击败了我们,那么他的胜场就超过了我们。因此,只有我们赢了他,我们才不会被他超过,排名才能尽量靠前。
在有限的时间内,我们自己一定会尽量击杀更多的对手。因此我们要在保证胜场的前提下去贪心。我们先计算出最多能够击杀多少玩家,然后判断这些玩家中能否包括第 x + 1 x+1 x+1名玩家,如果包括了,我们的排名就能够相对靠前。
因此,我们先按照 a i a_{i} ai从小到大进行结构体排序,计算胜利场次 x x x最多是多少,然后标记所有可能被击杀的玩家,判断这些玩家中有无第 x + 1 x+1 x+1名玩家。
如果没有,则答案为:
n
+
1
−
x
n+1-x
n+1−x;
如果有,则答案为:
(
n
+
1
−
x
)
−
1
=
n
−
x
(n+1-x)-1=n-x
(n+1−x)−1=n−x
即:
a
n
s
=
n
−
x
+
(
v
i
s
[
x
+
1
]
=
=
f
a
l
s
e
)
ans=n-x+(vis[x+1]==false)
ans=n−x+(vis[x+1]==false)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 5e5 + 5;
struct node{
ll id, time;
bool friend operator < (const node A, const node B){
return A.time < B.time;
}
}a[maxn];
bool vis[maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll t;
cin>>t;
while(t--){
ll n,m;
cin>>n>>m;
memset(vis,0,sizeof(vis));
for(ll i=1;i<=n;i++)cin>>a[i].time,a[i].id=i;
sort(a+1,a+1+n);
ll cur = m, x = 0;
for(ll i=1;i<=n;i++){
if(cur-a[i].time>=0){
cur-=a[i].time;
x++;
vis[a[i].id]=true;
}
else{
cur+=a[i-1].time;
if(cur-a[i].time>=0){
cur-=a[i].time;
vis[a[i].id]=true;
}
else break;
}
}
ll ans = n-x+(vis[x+1]==false);
cout<<ans<<endl;
}
return 0;
}
心得
这次又是只差一点就能出C题了,思路差不多,就是时间不够,如何判断第 x + 1 x+1 x+1名玩家是否能够被击败这一步,是比较难快速想到的。其思想是,找到所有可能的被击败的玩家,而不是找完当前的就不找了。这是一大坑点。