1. 程式人生 > >SRM709 div1 Xscoregame(狀壓dp)

SRM709 div1 Xscoregame(狀壓dp)

壓縮 out find 初始 reg max algo pan 是我

題目大意:

給定一個序列a,包含n個數(n<=15),每個數的大小小於等於50

初始時x = 0,讓你每次選a中的一個數y,使得x = x + x^y

問如何安排選擇的次序,使得最終結果最大。

考慮狀態壓縮,dp[S]表示選了S狀態的數的最大結果

我們發現這樣做是錯誤的,因為目前的最大並不意味最後的最大

但是我們會發現,y最大只有50,所以x的大於63的部分不會發生變化,只有小於64的部分會受到y異或的結果

所以我們用dp[S][t]表示:選了S狀態的數,小於64部分為t是否可行

然後用dp[S][64]代表大於64部分的大小

選第i個數加入的轉移就是,先求出小於64部分與A[i]異或的最大值Max

然後大於64部分的大小就是2*dp[S][64] + (Max>>6)

找到所有小於64部分的異或>>6大於1的值,然後更新那些可行的狀態即可

最後輸出就是大於64的部分<<6再加上小於64部分最大的可行解

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include 
<vector> #include <sstream> #include <typeinfo> #include <fstream> #include <queue> using namespace std; void print(int x){ cout<<"*"; while(x){ if(x&1) cout<<1; else cout<<0; x>>=1; } cout<<endl; }
long long dp[(1<<15) + 1][100]; queue<int> Q; int vis[(1<<15) + 1]; void Find(int S){ for(int i = 0; i < 64; i++) if(dp[S][i]) cout<<i<<" "; cout<<dp[S][64]; cout<<endl; } class Xscoregame { public: int getscore(vector<int> A) { int n = A.size(); Q.push(0); dp[0][0] = 1; while(!Q.empty()){ int S = Q.front(); Q.pop(); //print(S); //Find(S); for(int i = 0; i < n; i++){ if(S&(1<<i)) continue; int Max = 0; for(int j = 0; j < 64; j++){ if(!dp[S][j]) continue; Max = max(Max, j + (j^A[i])); } if(Max == 0) continue; if(dp[S|(1<<i)][64] > 2*dp[S][64] + (Max>>6)) continue; if(dp[S|(1<<i)][64] != 2*dp[S][64] + (Max>>6)) for(int j = 0; j < 64; j++) dp[S|(1<<i)][j] = 0; if(!vis[S|(1<<i)]) Q.push(S|(1<<i)); vis[S|(1<<i)] = 1; dp[S|(1<<i)][64] = 2*dp[S][64] + (Max>>6); for(int j = 0; j < 64; j++){ if(!dp[S][j]) continue; int temp = j + (j^A[i]); if((temp>>6) == (Max>>6)) dp[S|(1<<i)][temp&63] = 1; } } } long long ans = 0, temp = 0; for(int i = 63; i >= 0; i--) if(dp[(1<<n)-1][i]) { temp = i; break; } ans += (dp[(1<<n)-1][64]<<6) + temp; return ans; } };

SRM709 div1 Xscoregame(狀壓dp)