【JZOJ2679】跨时代【dfs】【状压】【背包】
题目大意:
题目链接:https://jzoj.net/senior/#main/show/2679
给出根线段的长度,选择其中一些线段组成一个长方形似的这个长方形的面积最大。
思路:
如果我们选择其中一些线段,设这些线段长度和为,若可以从这些已选线段中再找出一些线段使得这些线段长度为,那么这些长度和为的线段就可以分成两组长度和为的线段,而这两组线段就可以作为矩形的一组对边。
考虑暴力枚举每一条边选或不选,那么最终会得到选择为长、宽的两组集合。如果这两组集合的长度和都可以作为对边,那么这就形成了一个矩形。
所以预处理出每一个集合是否可以分为两个小集合。设为状压后的集合,为集合中的元素之和,那么我们就要在这些元素中选择其中一部分使得他们的和为。
这就是一个基础的01背包问题,套进去就可以了。
时间复杂度
代码:
#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;
}