码迷,mamicode.com
首页 > 其他好文 > 详细

【bzoj2064】分裂【压状dp】

时间:2014-12-02 00:01:08      阅读:439      评论:0      收藏:0      [点我收藏+]

标签:des   io   os   sp   for   on   数据   bs   代码   

Description

背景: 和久必分,分久必和。。。 题目描述: 中国历史上上分分和和次数非常多。。通读中国历史的WJMZBMR表示毫无压力。 同时经常搞OI的他把这个变成了一个数学模型。 假设中国的国土总和是不变的。 每个国家都可以用他的国土面积代替, 又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和。 一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积。 WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(分裂和合并各算一次操作),能让中国从当时状态到达现在的状态。

Input

第一行一个数n1,表示当时的块数,接下来n1个数分别表示各块的面积。 第二行一个数n2,表示现在的块,接下来n2个数分别表示各块的面积。

Output

一行一个数表示最小次数。

Sample Input

1 6
3 1 2 3

Sample Output

2
数据范围:
对于100%的数据,n1,n2<=10,每个数<=50
对于30%的数据,n1,n2<=6,

思路:想了很久还是找了解题报告,然后就醉了,齐刷刷一片只可意会不可言传,那就意会吧。。。。。又醉了 每份代码都差不多连pascal都是类似1<<i>>1这种神奇的写法

YY了很久,最优方案中出现的所有数一定是一开始两个数列中出现过的数,首先找一个操作数的上界,那一定是n+m,显然只要把第一个数列n次加起来加成一个数,然后再m次分割分成数列2即可

 如果已经有a数列里的一坨数的和等于b数列里一坨数的和的话,那先把这两坨数搞成相同的,就可以在上界的基础上减少两次操作,也就是将这两坨数的和合到总和里去,和从总和里分出来的那两次操作省去了,然后再看这两坨数里面最大的和相等的部分,这就是最优子结构了,就可以用DP愉快地搞定了

DP[S][T]表示a数列选中的方案为S,b数列中选中方案为T,两坨数相同的最大值,由上所述,每个相同能减少两次操作,因此答案就是n+m-dp[(1<<(n))-1][(1<<(m))-1]

 

 

#include<iostream>

#include<cstdio>

#define maxn 100009

using namespace std;

int a[maxn],b[maxn],suma[maxn],sumb[maxn];

int dp[2000][2000];

void dfs(int k,int state,int *a,int s,int *ans)

{

    if(k==a[0]+1)return ;

    ans[state<<(a[0]-k)]=s;

    dfs(k+1,state*2+1,a,s+a[k+1],ans);

    dfs(k+1,state*2,a,s,ans);

}

int main()

{

    scanf("%d",&a[0]);

    for(int i=1;i<=a[0];i++)scanf("%d",&a[i]);

    scanf("%d",&b[0]);

    for(int i=1;i<=b[0];i++)scanf("%d",&b[i]);

    dfs(0,0,a,0,suma);

    dfs(0,0,b,0,sumb);

    for(int i=1;i<=(1<<a[0])-1;i++)

        for(int j=1;j<=(1<<b[0])-1;j++)

        {

            for(int k=1;k<=a[0];k++)

            {

                int u=1<<(k-1);

                if((i&u)>0)dp[i][j]=max(dp[i][j],dp[i-u][j]);

            }

            for(int k=1;k<=b[0];k++)

            {

                int u=1<<(k-1);

                if((j&u)>0)dp[i][j]=max(dp[i][j],dp[i][j-u]);

            }

            if(suma[i]==sumb[j])dp[i][j]++;

        }

    printf("%d\n",a[0]+b[0]-2*dp[(1<<(a[0]))-1][(1<<(b[0]))-1]);

    return 0;

}

【bzoj2064】分裂【压状dp】

标签:des   io   os   sp   for   on   数据   bs   代码   

原文地址:http://www.cnblogs.com/philippica/p/4136176.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!