题干:
小a正在玩一款星际探索游戏,小a需要驾驶着飞船从11号星球出发前往nn号星球。其中每个星球有一个能量指数pp。星球ii能到达星球jj当且仅当pi>pjpi>pj。
同时小a的飞船还有一个耐久度tt,初始时为11号点的能量指数,若小a前往星球jj,那么飞船的耐久度会变为t⊕pjt⊕pj(即tt异或pjpj,关于其定义请自行百度)
小a想知道到达nn号星球时耐久度最大为多少
注意:对于每个位置来说,从它出发可以到达的位置仅与两者的pp有关,与下标无关
输入描述:
第一行一个整数nn,表示星球数
接下来一行有nn个整数,第ii个整数表示pipi
输出描述:
一个整数表示到达nn号星球时最大的耐久度
若不能到达nn号星球或到达时的最大耐久度为00则输出−1−1
示例1
输入
复制
3
457 456 23
输出
复制
478
说明
小a有两种方法到达33号星球
第一种:1→2→31→2→3,最终耐久度为457⊕456⊕23=22457⊕456⊕23=22
第二种:1→31→3,最终耐久度为457⊕23=478457⊕23=478
示例2
输入
复制
4
2 4 4 2
输出
复制
-1
示例3
输入
复制
5
234 233 123 2333 23
输出
复制
253
备注:
1⩽n,∀pi⩽3000
解题报告:
这题做法比较多样,比较好想的就是当成布尔背包去解,因为告诉你了最大值是3000,那就一个bool类型的dp数组判断能否得到就行了。因为异或不具有单调性(即正数异或一个正数得到的数不一定更大),,所以直接背包取max的做法显然是错误的。。还有就是这题可以直接类似spfa去求解,但是这题告诉你了顺序是从大到小走,所以可以直接排序然后直接判断就行了。具体解法就是排序然后把值位于a[1] a[n]之间的都加到b数组中,然后对b数组直接背包就行了,因为这样背包背出来的dp值如果是1,代表一定能凑出来,如果是0,代表一定凑不出来。(其实这样解的话就不需要排序了,,但是标程的解法是排序的。)
第二种方法是线性基,刚刚学会,,感觉超级好用。。代码附上、、
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int N = 3000 + 5;
ll read() {ll x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}return x*f;
}
int a[N],f[1<<12],b[N];
int main()
{int n = read();for(int i = 1;i <= n;++i) a[i] = read();int js = 0;int mx = a[1],mn = a[n];if(mx <= mn) {puts("-1");return 0;}f[mn ^ mx] = 1;f[0] = 1;for(int i = 1;i <= n;++i) {if(a[i] > mn && a[i] < mx) {b[++js] = a[i];}}int END = 1 << 12;for(int i = 1;i <= js;++i) {for(int j = END;j >= 0;--j) {f[j] = f[j ^ b[i]] | f[j];}}int ans = 0;for(int i = END;i >= 0;--i) {if(f[i] == 1) {ans = i;break;}}if(ans == 0) puts("-1");else printf("%d",ans);return 0;
}
线性基解法1:(这个query其实并不是很标准,,这题貌似是数据问题刚好放过去了。、、)
#include <bits/stdc++.h>
#define lld I64d
using namespace std ;
const int Bit = 12 ;
int N , A[3005] , Base[Bit+5] ;
inline void Insert( int Num ) {for( int i = Bit ; --i >= 0 ; )if( 1 << i & Num )if( not Base[i] ) {Base[i] = Num ;break ;}else Num ^= Base[i] ;
}
inline int Query( int Num ) {for( int i = Bit ; --i >= 0 ; )Num = max( Num , Num ^ Base[i] ) ;return Num ;
}
int main() {cin>>N;for(int i = 1 ; i <= N ; i++) cin >> A[i] ;if( A[1] <= A[N] ) {printf( "-1\n" ) ;return 0;}for(int i = 2 ; ++i < N ; )if( A[1] > A[i] and A[i] > A[N] )Insert( A[i] ) ;printf( "%d\n" , Query( A[1] ^ A[N] ) ) ;return 0;
}
线性基解法2:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 2e5 + 5;
int n,a[MAX];
vector<int> vv;
bool ok(int i) {if(a[i] > a[n] && a[i] < a[1]) return 1;else return 0;
}
int main() {cin>>n;for(int i = 1; i<=n; i++) cin>>a[i];if(a[1] <= a[n]) {puts("-1");return 0 ;}for(int i = 2; i<=n-1; i++) {if(ok(i)) vv.pb(a[i]);}int up = vv.size(),ans=0;ans = a[1]^a[n];if(up) {for(int i = 14,k=0; i>=0; i--) {for(int j = k; j<up; j++) {if((1<<i) & vv[j]) {swap(vv[j],vv[k]);break;}}if((1<<i) & vv[k]) {for(int j = k+1; j<up; j++) {if((1<<i) & vv[j]) vv[j] ^=vv[k];}k++;}}for(int i = 14,k=0; i>=0; i--) {if((1<<i) & vv[k]) ans = max(ans,ans^ vv[k]),k++;//或者 if (!(ans >> i & 1)) ans ^= vv[k];}}if(ans == 0) ans=-1;printf("%d\n",ans);return 0 ;
}
总结:
其实最后求解的时候不能直接取max我感觉