2019暑假集训DAY15(problem3.Miku)(状压)

(其实这道题就是bzoj2046改了题面子)

跨越时空的爱恋

Time Limit:1Sec Memory Limit:64MB Miku.cpp/in/out

Description

她就这样走了,随着樱花的飘散,她就这样走了.

平淡原来是感情最大的恩惠.于千万光年中的一瞬有幸在院子里的樱花树下见到来自另一个次元的她,却又在转瞬即逝的平淡点滴中与她分别.无涯的时空荒野里,相恋一季春天,她随樱花来,又伴樱花去.二次元风暴的突兀出现,带走了误入三次元的她.院子里的樱花也被席卷飘散,在空中聚合分离.来不及感叹相处的短暂,OY的眼前只剩下了满地残殇.一切都随风而逝了吗?OY似乎还记得初遇时樱花树每朵花瓣的模样.如果试着去复原,她还会不会在这棵与那时相同的樱花树,像那时一样奇迹般的降临呢?

从前的樱花树有n1个花簇,每个花簇的花朵数OY也还记得.二次元风暴席卷时,多次改变了樱花树的模样.每次改变或是把一个花簇吹开成了两簇,抑或是把两个花簇吹拢成了一簇.虽然花朵总量不变,但席卷后花簇的数量以及每个花簇的花朵数都发生了改变.

OY需要对现在的樱花树同样进行类次元风暴的操作来复原.如果说将一个花簇分成两簇,或者将两个花簇聚拢成一个花簇看作两种操作,OY想在尽可能少的操作次数中将樱花树复原.

没有人能预测命运的走向,如果你不选择做点什么的话.

Input

第一行一个数n1,表示从前的花簇数,接下来n1个数分别表示各花簇的花朵数。

第二行一个数n2,表示现在的花簇数,接下来n2个数分别表示各花簇的花朵数。

Output

输出最小次数

Sample Input

1 6

3 2 3 1

Sample Output

2

Sample Explanation

将2和1进行一次聚拢操作成3,再将3和3进行一次聚拢操作成6,一共2次操作.

数据范围

对于前30%的数据,n1,n2<=6.

对于100%的数据,n1,n2<=10,每个数<=50.


思路很清奇的状压,反正不是我能想到的==(参考

分析:

先考虑操作的最坏情况,把序列2全部聚拢再分成序列1需要操作n1+n2-2次

假如我们选择初始选择k0个的和恰好等于目标状态k个的和,可以单独让它们通过k0+k-2次操作达到目标状态,

剩下的通过n1-k0+n2-k-2次操作达到目标状态,总共操作n1+n2-4次

和原来相比减少了两次。

也就是说,如果能找到越多这样的块,我们减少的操作次数就越多

于是考虑状压,dp[i]表示i这个状态可以找到多少个这样的块,i可以从i这个状态减少任意一个1转移。

可以把n1,n2合并成一个序列,1和0表示选与不选,如果相加的和sum为0,说明找到了一个满足条件的块。

最终答案为n-dp[1<<(n)-1]

#include<bits/stdc++.h>
#define LL long long
#define INF 2100000000
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
int dp[1<<21],a[22],sum[1<<21];
int main()
{
    int n=read();
    for(int i=1;i<=n;++i)a[i]=read();
    int m=read();
    for(int i=1;i<=m;++i)
    {
        a[i+n]=read();
        a[i+n]=-a[i+n];
    }
    n+=m;
    for(int i=1;i<(1<<n);++i)
    {
        for(int j=1;j<=n;++j)
            if(i&(1<<j))dp[i]=max(dp[i],dp[i^(1<<j)]),sum[i]+=a[j];
          if(sum[i]==0)dp[i]++;
    }
    printf("%d
",n-2*dp[(1<<n)-1]);
    
} 
View Code
原文地址:https://www.cnblogs.com/yyys-/p/11267816.html