H两难抉择
题目是在两种操作里任选一种执行最多一次,并且操作可以针对数组里的任意元素,即选一种操作,可执行,也可以不执行,若执行,也最多只能执行一次,显而易见的是肯定是在最大元素乘以n和最大元素加n,里面挑选一个最大值进行数组的累加,但最后评测的时候有一个样例没有通过,说明我们考虑的不全面,所以在这里我们需要遍历所有可能的情况,选出最终的最大值
原错误代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
long long a[n];
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
int m=a[n-1]*n;
int b=a[n-1]+n;
if(m>b){
a[n-1]=m;
}
else a[n-1]=b;
long long ans=0;
for(int i=0;i<n;i++){
ans+=a[i];
}
cout<<ans;
return 0;
}
正确代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
long long a[n],ans=0;
for(int i=0;i<n;i++){
cin>>a[i];
ans+=a[i]; //不进行操作时的和
}
long long mx=ans;
for(int i=0;i<n;i++){ //枚举每种可能,找出最大值
mx=max({mx,ans+n,(ans-a[i])+a[i]*n}); //三个数中挑选出最大的数
}
cout<<mx;
return 0;
}
K
本题看起来复杂,定义联通块的大小为其所包含点个数,且最后剩下两个联通块大小的乘积为此图的代价,要最大化此图代价,因此我们肯定是要分成两部分,因此实际上只是求满足a+b=n这个条件对于的a和b的乘积最大值
对于a+b=n计算a,b最大乘积的方式:
当 n
为偶数时,最优分割是 n/2 * n/2
;
当 n
为奇数时,最优分割是 (n-1)/2 * (n+1)/2;
不确定n的奇偶性时,
最优分割是 n/2*(n-n/2)
原错误代码
(整形变量类型定义错误,导致中间运算过程数据溢出)
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;//应用long long
cin>>n>>m;
for(int i=0;i<m;i++){
int u,v;
cin>>u>>v;
}
int mx=0;//应用long long
for(int i=1;i<=n/2;i++){
mx=max(mx,i*(n-i));
}
cout<<mx;
return 0;
}
正确代码
cpp
法一(最优解)。。。。。。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
long long u,v;
cin>>u>>v;
}
cout<<n/2*(n-n/2);
return 0;
}
法二(改后代码)运用循环,时间复杂度较高。。。。。。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
long long u,v;
cin>>u>>v;
}
long long mx=0;
for(int i=1;i<=n/2;i++){
mx=max(mx,i*(n-i));
}
cout<<mx;
return 0;
}
I 除法移位
简化题目
1.操作次数可以优化到 n-1
次以下
循环右移的性质决定了:右移 k
次和右移 k % n
次的效果完全相同。
例如,数组长度为 n=5
时:
- 右移 5 次相当于没移(回到原始状态)
- 右移 6 次等价于右移 1 次(6 % 5 = 1)
因此,只需考虑 0 ≤ k ≤ n-1
次右移 ,但同时也要注意题目所给条件最多对数组 aa进行 tt次循环右移 操作,因此只需考虑 0 ≤ k ≤min(n-1,t)
次右移,后续的操作会进入循环,无需重复计算。
2. 第 i
次操作后的分子分析
每次右移后,数组的第一个元素(即分子)会发生变化。
右移 i
次后,原数组的第 (n - i) % n + 1
个元素会成为新的分子。
例如:
- 原数组:
[a₁, a₂, a₃, a₄, a₅]
- 右移 1 次后:新数组为
[a₅, a₁, a₂, a₃, a₄]
,分子为a₅
(即原数组的第 5 个元素) - 右移 2 次后:新数组为
[a₄, a₅, a₁, a₂, a₃]
,分子为a₄
(即原数组的第 4 个元素)
3. 表达式 S(i)
的转换
原表达式 S = a₁ ÷ a₂ ÷ ... ÷ aₙ
可以改写为:
右移 i
次后,分子变为 a[(n-i) % n +1]
,分母为剩余元素的乘积。
若所有元素均为正数,则最大化 S
等价于最大化分子,因为分子最大化之后,对应的分母就会减小,S整体变大。
4. 关键结论
在 0≤i≤
min(n-1,t)
的范围内,找到使得 a[(n-i) % n +1]
最大的 i
,即为最优解。
正确代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,t;
cin>>n>>t;
long long a[n+1];
for(int i=1;i<=n;i++){
cin>>a[i];
}
int cnt=0,mx=0;//cnt是操作次数,mx记录此时最大的分子,即右移后数组第一个元素
//循环遍历所有可能右移的次数,一定注意从0开始,因为也可以不进行操作,如示例一
for(int i=0;i<=min(n-1,t);i++){
if(a[(n-i)%n+1]>mx){
mx=a[(n-i)%n+1];//更新当前最大值,经过i次右移后,分子,即右移后数组第一个元素变为a[(n-i)%n+1]
cnt=i; //更新最优操作次数
}
}
cout<<cnt;
return 0;
}
F 两难抉择新编
分析题目
与H题类似,只是由数组总和变成了数组异或和,在 C++ 中^是按位异或运算符,因此只需要把H题中的+改为^,于是用sum
^=a[i]
来累积计算数组元素的异或和,除此之外, 题目还需要计算修改数组后的最大异或和,可以 利用异或的性质:若原异或和为 sum
,修改某个元素 a[i]
为 new_val
,则新异或和为:sum ^ a[i] ^ new_val,这是因为:
- 原数组的异或和包含
a[i]
- 先异或
a[i]
相当于从总和中移除a[i]
(即sum ^ a[i]
) - 再异或
new_val
相当于加入新值
正确代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
long long a[n+1],sum=0;//数组下标从1开始,则数组a需要开n+1个数组空间
for(int i=1;i<=n;i++){
cin>>a[i];
sum^=a[i]; //计算原始数组的异或和
}
long long mx=sum;// 把最大异或和初始化为原始数组的异或和,使不进行操作的情况包含在内
for(int i=1;i<=n;i++){ // 遍历每个可能的数组元素
for(int j=1;j<=(n/i);j++){ // 对于每个元素a[i],尝试所有可能的x值(1到n/i)
mx=max({mx,sum^a[i]^(a[i]+j),sum^a[i]^(a[i]*j)});//更新最大的数组异或和
}
}
cout<<mx;
return 0;
}