題目鏈接:
題意:
一個n×n的矩陣A,每個格子上有一個有一個數Ai,j。入口在左上角的(1,1)處,出口在右下角的(n,n)處。每一步都只能向下或向右移動一格。最後能獲得的經驗值爲初始經驗e與路徑上經過的所有數的權值異或和。求筱瑪最大可能獲得的經驗值。
思路:
如果純暴力,dfs整張圖,複雜度O(2^(2n)),數量級最壞情況下爲O(2^40),肯定會TLE。
但如果只暴力搜半張圖,最壞爲O(2^20),時間沒有問題。因此,我們先從(1,1)開始dfs暴力遍歷上半張圖(上半部分,以對角線(從右上到左下)爲分割線),對角線上的每個點開一棵字典樹,存到這個點的所有可能情況。然後我們再從(n,n)開始暴力遍歷下半張圖,每當搜到對角線上的點時就去該字典樹上求異或最大值(複雜度O(logk)= 31,k爲字典樹節點個數),取所有最大值中的最大值。
總複雜度O(2^20 * logk)
注意:字典樹的空間大小儘可能大,這題有毒......
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 5e6+10;
int n;
ll e;
ll mp[25][25];
int tree[25][MAX][2];
int tot=0;
ll ans=0;
void add(int id,ll x)
{
int root=0;
for(int i=30;i>=0;i--){
int p=(x>>i)&1;
if(!tree[id][root][p]) tree[id][root][p]=++tot;
root=tree[id][root][p];
}
}
ll query(int id,ll x)
{
int root=0;
ll val=0;
for(int i=30;i>=0;i--){
int p=(x>>i)&1;
if(tree[id][root][p^1]){
val+=(1ll<<i);
root=tree[id][root][p^1];
}
else{
root=tree[id][root][p];
}
}
return val;
}
void dfs1(int x,int y,int step,ll val)
{
if(step==n){
add(x,val^mp[x][y]);
return;
}
if(y+1<=n) dfs1(x,y+1,step+1,val^mp[x][y]);
if(x+1<=n) dfs1(x+1,y,step+1,val^mp[x][y]);
}
void dfs2(int x,int y,int step,ll val)
{
if(step==n){
ans=max(ans,query(x,val));
return;
}
if(y-1>=1) dfs2(x,y-1,step+1,val^mp[x][y]);
if(x-1>=1) dfs2(x-1,y,step+1,val^mp[x][y]);
}
int main()
{
scanf("%d%lld",&n,&e);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lld",&mp[i][j]);
tot=0;
ans=0;
dfs1(1,1,1,e);
dfs2(n,n,1,0);
printf("%lld\n",ans);
return 0;
}