1. 程式人生 > >HDU 5798 Stabilization(dfs)

HDU 5798 Stabilization(dfs)

Description
給出一個序列Ai,可以讓每個Ai異或上一個x使得這裡寫圖片描述最小,問最小值以及使得該值最小的最小x值
Input
第一行一整數T表示用例組數,每組用例首先輸入序列長度n,之後n個整數Ai表示該序列(T<=20,0<=Ai<2^20,1<=n<=10^5)
Output
對於每組用例,輸出這裡寫圖片描述的最小值以及使得該值最小的最小x值
Sample Input
2
3
0 3 0
3
1 3 1
Sample Output
1 2
0 4
Solution
看見異或考慮拆位,對於兩個數來說,A[i]和A[i+1]同時異或一個x,其二進位制表示中相同的位做差值仍為0,故之後只考慮不同的位,最後兩者做完差後的值主要看兩者最高位誰是1,例如第i位是最高位,那麼這一位對答案的貢獻必然是2^i,而一個較低位j對答案的貢獻需要看x的第j位和第i位是否相同,相同則為其本來的貢獻,不同則需加個負號,例如第一個數第i為是1,第j位是0,第二個數第i為是0,第j位是1,那麼這異或前這兩位的貢獻分別是2^i和-2^j,而異或後,第i位貢獻依舊是2^i,第j位的貢獻取決於x第i位和第j位是否相同,相同就還是-2^j,不相同就是2^j,於是對這n-1對數做以上處理,先將對應位在異或前的貢獻存在一個20*20的矩陣中,之後2^20列舉x每一位,通過dfs+剪枝得到最小值以及使得值最小的最小x值
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 111111
int T,n,a[maxn],deep,x;
ll num[22][22],ans;
void dfs(int pos,ll sum,int cnt)
{
    if(pos==deep+1)
    {
        if(sum<ans)ans=sum,x=cnt;
        else
if(sum==ans)x=min(x,cnt); return ; } if(sum>ans)return ; for(int i=0;i<2;i++) { ll temp_sum=sum+num[pos][pos]; for(int j=0;j<pos;j++) if(((cnt>>j)&1)==i)temp_sum+=num[pos][j]; else temp_sum-=num[pos][j]; dfs(pos+1
,temp_sum,cnt+(i<<pos)); } } int main() { scanf("%d",&T); while(T--) { ans=deep=0; memset(num,0,sizeof(num)); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<n;i++) { int ta=max(a[i],a[i+1]),tb=min(a[i],a[i+1]); ans+=ta-tb; int high=19; while(high>=0&&~((ta^tb)>>high)&1)high--; deep=max(deep,high); for(int j=high;j>=0;j--) num[high][j]+=(ta>>j&1)-(tb>>j&1); } for(int i=0;i<=deep;i++) for(int j=0;j<=i;j++) num[i][j]<<=j; dfs(0,0,0); printf("%d %I64d\n",x,ans); } return 0; }