BZOJ2064状态压缩动态规划

n1和n2都小于等于10,考虑状态压缩动态规划

预处理:

d[ i ][ j ]表示开始状态的子集i,变成结束状态的子集j所需要的步数。(这里需要i的面积和与j的面积和相等,所需要的步数是暴力合并i和暴力拆分j的步数)

DP:

F[ i ][ j ]表示当前剩余状态开始状态的i,和结束状态的j。

枚举i的子集p , j ^ (1 << (n2-1))的子集q // q是全集j的补集的子集

        主动递推:

F[ i ^ p ][ j | q ] = min{ F[ i ][ j ] + d[ p ][ q ] };

注意:

1、动态规划的无后效性,即i从大到小枚举,j从小到大枚举。

2、注意枚举子集的方法

3、要判断i + j的面积是否为全集的面积,否则会TLE


#include <iostream>
#include <cstdio>
#include <cstring>
#define N 2049

using namespace std;

int x[N],y[N],n1,n2;
int d[N][N],F[N][N],S,T;

int lowbit(int x) { return (x & (-x)); }

int c(int x) { 
	int t = 0; 
	while (x) {	t++; x -= lowbit(x); } 
	return t; 
}

int sumx(int i) {
	int t = 0 , tmp = 0;
	while (i) {
		t = lowbit(i);
		tmp += x[t];
		i -= t;
	}
	return tmp;
}

int sumy(int i) {
	int t = 0 , tmp = 0;
	while (i) {
		 t = lowbit(i);
		 tmp += y[t];
		 i -= t;
	}
	return tmp;
}

void Pre() {
	
	for (int i=1;i<=(1<<n1)-1;i++)
		for (int j=1;j<=(1<<n2)-1;j++)
			if (sumx(i) == sumy(j)){
				d[i][j] = c(i) - 1 + c(j) - 1;
				//printf("%d %d %d\n",i,j,d[i][j]);	
			}
	
}

void DP() {
	int dhr = (1 << n2) - 1;
	for (int i=(1<<n1)-1;i>=0;i--)
		for (int j=0;j<=(1<<n2)-1;j++) 
			if (sumx(i) + sumy(j) == sumx(S))
				for (int p=i;p;p=((p-1)&i))
					for (int q=j^dhr;q;q=((q-1)&(j^dhr))) {
						F[i^p][j|q] = min(F[i][j] + d[p][q] , F[i^p][j|q]);
					}
					
}

int main()
{
	memset(F,127/3,sizeof(F));
	memset(d,127/3,sizeof(d));
	
	scanf("%d",&n1);
	for (int i=1;i<=n1;i++) scanf("%d",&x[1 << (i-1)]);
	scanf("%d",&n2);
	for (int i=1;i<=n2;i++) scanf("%d",&y[1 << (i-1)]);
	
    S = (1<<n1) - 1; T = (1<<n2) - 1;
	
	F[S][0] = 0;
	
	Pre(); DP();
	
	printf("%d\n",F[0][T]);
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值