牛客周赛 Round 135题解(不含F)
B.Maximize The Count
题意:
给出n个从1到n的数字序列,从中选择出子序列使子序列中任意的两个相邻的数字的数值之差和在原本的序列中的下标之差相等,然后求出最多有多少个这样的子序列。
子序列是从原序列中选出任意个数字保持他们的相对位置的序列
思路:
我们选出原序列为Pi和Pj的两个数字他们对应的数值是aPi和aPj,那么我们要满足公式aPi-aPj=Pi-Pj,我们对公式进行处理变成aPi-Pi=aPj-Pj。那么我们可以发现只需要选择的数字i的aPi-Pi相同那么就说明他们一定在子序列中满足条件,那么这个值可以看做一个特征,我们只需要计算有多少个特征值。
代码:
点击查看代码
int n;
cin>>n;
vector<int>a(n+1);
unordered_map<int,int>mp;
for(int i=1;i<=n;i++){
cin>>a[i];
int key=a[i]-i;
if(mp.find(key)==mp.end()){
mp[key]=1;
}
else
mp[key]++;
}
int ans=0;
for(auto& it:mp){
if(ans<it.second)
ans=it.second;
}
cout<<ans<<endl;
C.Permutation Swapping
题意:
现在有一个n长度的序列,里面包含1~n个数字,且每个数字只会出现一次,现在我们可以对这个序列中任意两个不相邻的数字进行交换操作,问我们最后是否可以将这个序列变成从1到n的升序排列,可以输出YES否则NO
思路:
首先注意到对于这个序列的操作可能有很多种顺序可以使我们的序列得到最终的升序,所以不打算考虑模拟的操作来看是否可以得到最终的序列,所以应该从这道题本身的规律出发。可以注意到当n为3的时候是一个很特殊的序列,因为我们能进行的交换操作就只有第一个和第三个,如果中间的数字不是2的话无论交换多少次都是无法得到最终的序列的,那么我们就应该按照3来划分界限。
假n大于3那么我们可以验证无论n是多少我们总可以通过交换把第一个位置给排出来,剩下n-1个数字,那么我们总可以把第二个数字排出来,那么以此类推我们最终到达只剩下三个数字的时候,只需验证一下是否可以把三个中的第一个排出来,事实证明是可以的,那么剩下两个也是总可以通过有限的交换排好,那么我们所有的数字都可以排好,只要n比3大。那么等于3的情况我们已经讨论过了,剩下还有两个特殊的讨论,就是n分别为1和2的时候,最后特殊判断可以得到答案
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(n>3)
cout<<"YES"<<endl;
else if(n==3){
if(a[2]==2)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}else if(n==2){
if(a[1]!=1)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}else if(n==1)
cout<<"YES"<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
D.Two Options
题意:
有t个n长度的数组,我们可以对数组中的数字进行一下两种操作,一是选择一个数字把这个数字加1,二是选择一个数字加1的同时选择另一个数字减1,最终时整个数组中的数字都相等,输出操作的最少的次数
思路:
我们先观察两个操作,1操作会导致整个数组的总和变大,而操作2不会。假设我们最终的数组每个数字都是x那么我们总和就是n×x,我们计算最开始的总和sum,如果sum刚好等于n×x那么说明我们只需要进行操作2,那么我们对于每一个小于x的数字操作2,那么一定有对于的大于x的数字可以同时减去,最终操作的次数就是小于x的数字差距的总和,而如果最终的总和和现在的不相等的话我们以为要尽量最小操作的次数,那么肯定是x越小越好,而又必须是一个整数,所以对sum/n向上取整就可以了,这样最终的x是尽量最小的,同样的,以为x变大了,那么每一个小于x的数字与x的差距就是需要的操作的次数,多出来的是单独执行的操作1的次数
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve(){
int n;
cin>>n;
vector<ll>a(n);
ll sum=0;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
}
ll k;
if(sum>=0)
k=(sum+n-1)/n;
else
k=sum/n;
ll ans=0;
for(int i=0;i<n;i++){
if(a[i]<k)
ans+=k-a[i];
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
E.Not Equal
题意:
t组数据,每一组给出三个长度为n的数组a,b,c分别代表一组数字和将这组数字的每个位置+1的代价,和每个位置-1的代价,要求我们对原本的数字进行修改使每个相邻的数字都不相等,求出每一组数据的最小代价
思路:
首先我们考虑一个位置上的数字的最多需要的调整次数,我们可以发现一个数字最多调整两次就可以与周围的数字都不相同,那么我们可以发现我们对每个位置上的数字向上和向下取两个偏差,加上本来的自己就有五个数字,如果我们最终的数字在这个范围之外,我们能证明在这个范围之内,一定有一个数字不和左边和右边的数字相同,那么这个数字可以成为新的最终的数字,而这个改变的次数一定是小于最开始的数字的。
我们设置两个状态,一个是上一个位置的每个的取值和在这个情况下的代价的数组,一个是现在这个位置的每个取值和在这个情况下的代价的数组,而现在这个位置的最小的代价就是现在这个位置上的可能的数字的的代价加上上一个位置的最小的代价。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll INF=1e18;
void solve(){
int n;
cin>>n;
vector<ll>a(n),b(n),c(n);
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)cin>>b[i];
for(int i=0;i<n;i++)cin>>c[i];
vector<ll>pass_v(5),pass_dp(5);
int pass_size=0;
for(int i=0;i<n;i++){
vector<ll>now_v(5),now_dp(5);
int now_size=0;
for(int j=a[i]-2;j<=a[i]+2;j++){
if(j>=1){
now_v[now_size++]=j;
}
}
for(int j=0;j<now_size;j++){
ll may=now_v[j],cost=0;
if(may>a[i]){
cost=(may-a[i])*b[i];
}else{
cost=(a[i]-may)*c[i];
}
if(i==0){
now_dp[j]=cost;
}else{
ll mi=INF;
for(int k=0;k<pass_size;k++){
if(pass_v[k]!=may)
mi=min(mi,pass_dp[k]);
}
now_dp[j]=mi+cost;
}
}
for(int j=0;j<now_size;j++){
pass_dp[j]=now_dp[j];
pass_v[j]=now_v[j];
}
pass_size=now_size;
}
ll ans=INF;
for(int i=0;i<pass_size;i++){
if(ans>pass_dp[i])
ans=pass_dp[i];
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
浙公网安备 33010602011771号