[2020.7.24NOIP提高组模拟T3]终章-剑之魂
【题目描述】
终焉的试炼即将到来,作为一名有修养的剑士,虽然没有习得n刀流但是二刀流还是没问题的。然而我也是个剑的收藏者,家里屯着n把剑,每一把剑都有一个灵魂值a[i],由于一些剑之间可能有共鸣,所以我需要两把契合度最高的剑。据剑圣所说,两把编号为i,j剑的契合度为a[i] & a[j]。如何深得剑的灵魂呢?
【输入】
第一行一个整数n,代表藏剑数。
第二行n个整数,第i个整数表示a[i]。
【输出】
输出包含一个正整数,最好的两把剑的契合度。
【样例输入】
5
12 5 6 3 1
【样例输出】
4
【数据范围】
对于40%的数据 n ≤ 1,000
对于100%的数据 n ≤ 1,000,000,0 ≤ a[i] < 231
【解题思路1】
直接枚举所有情况,时间复杂度O(n2),会超时,得分40分
【解题思路2】
思路1的优化,先用sort将a[i]从大到小排列,因为ans的值为a[i]&a[j]的最大值,所以,当 ans>=a[i] 或 ans>=a[j] 时,小于等于ans的a数组的值(包括a[i]和a[j])无法使ans的值变大,所以跳过(break)
实际时间复杂度为O(n log n)+O(玄学)
最坏情况下时间复杂度会退化为O(n2),但由于本题数据过水,能得100分
#include<bits/stdc++.h> #include<fstream> using namespace std; int n,ans; int w[1000005]; ifstream fin("sword.in"); ofstream fout("sword.out"); inline bool cmp(int a,int b){ return a>b; } int main(){ fin>>n; for(int i=1;i<=n;i++) fin>>w[i]; sort(w+1,w+n+1,cmp); for(int i=1;i<=n;i++){ if(w[i]<=ans) break; for(int j=i+1;j<=n;j++){ if(w[j]<=ans) break; ans=max(ans,w[i]&w[j]); } } fout<<ans; return 0; }
【解题思路3】(正解)
无需排序,时间复杂度O(30*n)
易知:1+2+4+……+2^(n-1)< 2^n
因此,就算只有从右往左第n位取1,取得的值也比倒数第1位到第(n-1)位全取1大(高位的数能取必须取)
因为 a[i]<2^31 ,所以先从高位(倒数第 i=30 位)枚举到低位(倒数第i=0 位),再枚举 a[j](j=1~n)
如果现在的 ans & a[j]==ans ,说明a[j]不会使ans的值变小
再如果 a[j] & (1<<i)>0 ,即a[j]的二进制从右往左第(i+1)取1,表示a[j]有可能使ans的值变大。
如果满足以上两种情况的a[j]有不少于两组,表示ans可以被满足以上两种情况的 a[j] &a[j+x] (x不定)更新 ,更新后的ans比更新前至少大(1<<i)(回看第二种情况)。
#include<bits/stdc++.h>
#include<fstream>
using namespace std;
int n,ans;
int w[1000005];
ifstream fin("sword.in");
ofstream fout("sword.out");
int main(){
fin>>n;
for(int i=1;i<=n;i++)
fin>>w[i];
for(int i=30;i>=0;i--){
int dsc=0;
for(int j=1;j<=n;j++)
if(((ans&w[j])==ans)&&(w[j]&(1<<i))) ++dsc;
if(dsc>=2) ans+=(1<<i);
}
fout<<ans;
return 0;
}
2020-07-24