bzoj 2064 分裂 状压DP

2064: 分裂点击打开链接

Description

背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。

Input

第一行一个数n1,表示当时的块数,接下来n1个数分别表示各块的面积。 第二行一个数n2,表示现在的块,接下来n2个数分别表示各块的面积。

Output

一行一个数表示最小次数。

题解:

关键:最坏全部合并后分裂,共n+m-2次

        如果有两个子集和对应相等,则省两次操作。

        所以用f[S1][S2]表示两个集合的最有分割,枚举子集转移?

        可以用枚举一个新元素来代替

         f[S1][S2]= max(f[S1^i][S2],f[S1][S2^j])+1。

         同样可以枚举出最优划分


注意:想题尽量自己想,少看题解,这么简单的题应该能想出来啊

        代码一次写对,少犯细节错误。该题集合从1开始枚举,显然空集无意义


#include<bits/stdc++.h>
using namespace std;
#define maxn 2020
#define lowbit(x) (x&(-x))

int sum[2][maxn],n1,n2,len[2];
int f[maxn][maxn];
int a[maxn];

void init(){
	for (int i = 0 ; i < 2 ; i++){
		for (int j = 0 ; j < len[i] ; j++){
			sum[i][j] = sum[i][j ^ lowbit(j)] + sum[i][lowbit(j)];
		}
	}
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d",&n1) , len[0] = 1 << n1;
	for (int i = 1 ; i <= n1 ; i++) scanf("%d",&a[i]) , sum[0][1 << (i - 1)] = a[i];
	scanf("%d",&n2) , len[1] = 1 << n2;
	for (int i = 1 ; i <= n2 ; i++) scanf("%d",&a[i]) , sum[1][1 << (i - 1)] = a[i];
	init();
	for (int i = 1 ; i < len[0] ; i++){
		for (int j = 1 ; j < len[1] ; j++){
			for (int k = 0 ; k < n1 ; k++)
				if ( i & (1 << k) ) f[i][j] = max(f[i][j],f[i ^ (1 << k)][j]);
			for (int k = 0 ; k < n2 ; k++)
				if ( j & (1 << k) ) f[i][j] = max(f[i][j],f[i][j ^ (1 << k)]);
			if ( sum[0][i] == sum[1][j] ) f[i][j]++;
		}
	}
	printf("%d\n",n1 + n2 - 2 * f[len[0] - 1][len[1] - 1]);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/80915195
今日推荐