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

11 篇文章 0 订阅

题目大意:

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


思路:

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


代码:

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值