20191212 A 状压DP

47 篇文章 0 订阅

有一个数组 a a a,其中有 n n n个元素,现在请构造一个数组 b b b,其中也有 n n n个元素,使得数组 b b b中所有元素的异或值为 0 0 0,且 a i a_i ai b i b_i bi的距离之和最小。
T < = 6 , n < = 15 , a i < = 1 e 9 T<=6 , n <=15 ,a_i<=1e9 T<=6,n<=15,ai<=1e9

这个。
从高位到低位 D P DP DP
当所有数在该位的异或和为 0 0 0,可以直接看下一位。
否则,显然最优决策一定是把一个在该位(第 i i i位, 2 i 2^i 2i)为 1 1 1的数变为 2 i − 1 2^i-1 2i1
或者把一个在该位为 0 0 0的数变为 2 i 2^i 2i
那么这就是一个 O ( n ) O(n) O(n)的转移过程。
这些变化我们可以用 3 n 3^n 3n的三进制状态表示。
复杂度为 O ( T w n 3 n ) O(Twn3^n) O(Twn3n),其中 w w w为最高位数。
显然过不了。
可以用队列拓展来刷表转移。
但是状态数还是很多。
我们可以玄学。
如果我们当前要拓展的状态,和他同一层的最小值 < < <当前状态 D P DP DP − n ∗ 2 i + 1 -n*2^{i+1} n2i+1,那么我们是可以不拓展当前状态的,因为不可能追得上。

然后跑的比 s t d std std还快。
s t d std std是发现变为 2 i 2^i 2i和变为 2 i − 1 2^i-1 2i1有相似点可以用 2 n 2^n 2n的状态存储并加一维维护异或和。
O ( T w n 2 n ) O(Twn2^n) O(Twn2n)

A C   C o d e s \rm AC \ Codes AC Codes
O ( T w n 3 n ) O(Twn3^n) O(Twn3n)

#include<bits/stdc++.h>
#define maxn 15
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;

int n,a[maxn],pw[maxn]={1};
LL f[2][14348908],sum=0,pmn[2];
int q[2][14348908],R[2];
int b[maxn],c[maxn],now,pre,mx;

void check(int u,LL c){
	if(f[now][u] == inf) q[now][R[now]++] = u;
	f[now][u] = min(f[now][u] , c);
	pmn[now] = min(pmn[now] , c);
}

int main(){
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	int T;
	for(int i=1;i<maxn;i++) pw[i] = pw[i-1] * 3;
	memset(f,0x3f,sizeof f);
	for(scanf("%d",&T);T--;){
		scanf("%d",&n);
		mx = 0;
		for(int i=0;i<n;i++) scanf("%d",&a[i]),mx=max(mx,a[i]);
		now=1,pre=0;
		f[now][0]=0;
		q[now][R[now]++]=0;
		pmn[now] = pmn[pre] = inf;
		int MX = 0;
		for(int i=30;i>=0;i--){
			bool flg = 0;
			if((1<<i)>mx) continue;
			swap(now,pre);
			for(int u;R[pre];){
				u=q[pre][--R[pre]];
				if(f[pre][u] < pmn[pre] + n * (2ll<<i)){
					flg = 0;
					for(int j=0,v=u;j<n;j++,v/=3){
						c[j]=b[j]=v%3;
						if(b[j] == 0) flg ^= (a[j]>>i&1);
						else if(b[j] == 1) flg ^= 1;
					}
					if(flg){ 
						for(int j=0;j<n;j++){
							int cst=0;
							if(b[j]==0){
								if(a[j]>>i&1) c[j]=1,cst=a[j]-(1<<i)+1;
								else c[j]=2,cst=(1<<i)-a[j];
								int nsta = u + (c[j]-b[j]) * pw[j];
								check(nsta,f[pre][u]+cst);
							}
						}
						if(u) check(u,f[pre][u]+(1<<i));
					}
					else{
						check(u,f[pre][u]);
					}
				}
				f[pre][u]=inf;
			}
			MX = max(MX , R[now]);
			pmn[pre] = inf;
			for(int j=0;j<n;j++) a[j] %= (1<<i);
		}
		printf("%d\n",MX);
		LL ans = inf;
		for(;R[now];){
			ans = min(ans , f[now][q[now][R[now]-1]]);
			f[now][q[now][--R[now]]]=inf;
		}
		printf("%lld\n",ans);
	}
}

O ( T w n 2 n ) 的 s t d O(Twn2^n)的std O(Twn2n)std

#include <cstdio>
#include <cstring>
#include <algorithm>

#define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i)
#define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i)
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define bin(x) (1<<(x))
//#define LX_JUDGE

using namespace std;
typedef long long LL;
typedef pair<int, int> pii;

template<typename T> inline void upmax(T &x, T y) { x < y ? x = y : 0; }
template<typename T> inline void upmin(T &x, T y) { x > y ? x = y : 0; }

template<typename T>
inline void read(T &x) {
	char c;
	while ((c = getchar()) < '0' || c > '9');
	for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0');
}

const int inf = 0x3f3f3f3f;
const int maxn = 15;

int dp[2][bin(maxn) + 1][2][2];
int a[maxn], n;

void Solve() {
	read(n);
	rep (i, 0, n - 1) {
		read(a[i]);
	}
	memset(dp[0], 0x3f, sizeof(dp[0]));
	dp[0][0][0][0] = 0;
	int fr = 0, to = 1;
	down (s, 30, 0) {
		rep (i, 0, n - 1) {
			int v = (a[i] >> s) & 1;
			int cost = a[i] & (bin(s) - 1);
			cost = !v ? bin(s) - cost : cost + 1;
			memset(dp[to], 0x3f, sizeof(dp[to]));
			rep (j, 0, bin(n) - 1) {
				rep (l, 0, 1) rep (o, 0, 1) {
					int x = dp[fr][j][l][o];
					if (j & bin(i)) {
						upmin(dp[to][j][l][o], x);
						upmin(dp[to][j][l ^ 1][o], x + bin(s));
					} else {
						upmin(dp[to][j][l ^ v][o], x);
						upmin(dp[to][j | bin(i)][l ^ v ^ 1][o ^ v], x + cost);
					}
				}
			}
			swap(fr, to);
		}
		rep (j, 0, bin(n) - 1) {
			dp[fr][j][1][0] = inf;
			dp[fr][j][1][1] = dp[fr][j][0][1];
			dp[fr][j][0][1] = inf;
		}
	}
	int ans = inf;
	rep (i, 0, bin(n) - 1) {
		upmin(ans, min(dp[fr][i][0][0], dp[fr][i][1][1]));
	}
	printf("%d\n", ans);
}

int main() {
#ifdef LX_JUDGE
	freopen("in.txt", "r", stdin);
#endif
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	int kase;
	read(kase);
	while (kase--) {
		Solve();
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值