【JZOJ2679】跨时代【dfs】【状压】【背包】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/2679
给出nn根线段的长度,选择其中一些线段组成一个长方形似的这个长方形的面积最大。


思路:

如果我们选择其中一些线段,设这些线段长度和为kk,若可以从这些已选线段中再找出一些线段使得这些线段长度为k2\frac{k}{2},那么这些长度和为kk的线段就可以分成两组长度和为k2\frac{k}{2}的线段,而这两组线段就可以作为矩形的一组对边。
考虑O(3n)O(3^n)暴力枚举每一条边选或不选,那么最终会得到选择为长、宽的两组集合。如果这两组集合的长度和都可以作为对边,那么这就形成了一个矩形。
所以预处理出每一个集合是否可以分为两个小集合。设ss为状压后的集合,f[s]f[s]为集合中的元素之和,那么我们就要在这些元素中选择其中一部分使得他们的和为f[s]2\frac{f[s]}{2}
这就是一个基础的01背包问题,套进去就可以了。
时间复杂度O(3n)O(3^n)


代码:

#include <cstdio>
#include <cstring>
using namespace std;

const int N=20,MAXN=100010,M=250;
int n,ans,maxn,sum,a[N],f[MAXN];
bool g[M],p[MAXN];

void dfs(int x,int s1,int s2,int S1,int S2)
{
	if (x>n)
	{
		if (p[S1] && p[S2] && s1/2*s2/2>ans) ans=s1/2*s2/2;
		return;
	}
	dfs(x+1,s1,s2,S1,S2);
	dfs(x+1,s1+a[x],s2,S1|(1<<x-1),S2);
	dfs(x+1,s1,s2+a[x],S1,S2|(1<<x-1));
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	maxn=(1<<n)-1;
	for (int i=1;i<=maxn;i++)
	{
		for (int j=1;j<=n;j++)
			if (i&(1<<j-1)) f[i]+=a[j];
		if (f[i]&1) continue;
		memset(g,0,sizeof(g));
		g[0]=1;
		for (int k=1;k<=n;k++)
			for (int j=f[i]/2;j>=a[k];j--)
				if ((i&(1<<k-1))) g[j]|=g[j-a[k]];
		if (g[f[i]/2]) p[i]=1;
	}
	dfs(1,0,0,0,0);
	if (ans) printf("%d",ans);
		else printf("No Solution");
	return 0;
}
posted @ 2019-07-06 20:01  全OI最菜  阅读(92)  评论(0编辑  收藏  举报