bzoj 2064 分裂 - 状压dp

考虑n=m并且两两数字对应相等的时候答案是0
那么如果我们能把两组数字每一组都划分成k组使得这k组对应相等的话那么答案就可以省去2k
如果有一组内部黑可以继续划分那么答案可以继续减少2
因此问题转化为求最大的k
朴素的做法是3的2n次,加上一些剪枝即可
但其实我们令dp[s][t]在s集合的和不等于t集合的和的时候也有意义,只在sum(s)=sum(t)的时候dp++即可。
这样可能会使得某一次应该+1的时候没有+1,但是只会使得答案错误的计算小了,而最优解一定会被某种转移计算出来,因此事对的

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
#define MAXS 1100
#define N 15
#define Max(x,y) x=max(x,y)
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int dp[MAXS][MAXS],con[MAXS][N],a[N],b[N],id[MAXS],vs[MAXS],vt[MAXS],lb[MAXS];
int main()
{
    int n,m,s1,s2;dp[0][0]=-1;
    n=inn(),s1=1<<n;rep(i,0,n) a[i]=inn();
    m=inn(),s2=1<<m;rep(i,0,m) b[i]=inn();
    rep(i,0,max(n,m)) id[1<<i]=i;rep(i,1,max(s1,s2)) lb[i]=i&-i;
    rep(i,0,max(s1,s2)) rep(j,0,max(n,m)) con[i][j]=(i>>j)&1;
    rep(i,1,s1) vs[i]=vs[i^lb[i]]+a[id[lb[i]]];
    rep(i,1,s2) vt[i]=vt[i^lb[i]]+b[id[lb[i]]];
    rep(s,0,s1) rep(t,0,s2)
    {
        if(vs[s]==vt[t]) dp[s][t]++;
        rep(i,0,n) if(!con[s][i]) Max(dp[s|(1<<i)][t],dp[s][t]);
        rep(i,0,m) if(!con[t][i]) Max(dp[s][t|(1<<i)],dp[s][t]);
    }
    return !printf("%d\n",n+m-2*dp[s1-1][s2-1]);
}

猜你喜欢

转载自blog.csdn.net/mys_c_k/article/details/80799602
今日推荐