状压DP 小W选书籍

 本人水平有限,题解不到为处,请多多谅解

本蒟蒻谢谢大家观看

题目:

Problem H: 小W选书籍

Time Limit: 15 Sec  Memory Limit: 512 MB
Submit: 60  Solved: 21
[Submit][Status][Web Board]

Description

小W是一个爱读书的好学生。
众所周知,小W有着很多的书籍,而她也经常在学校里读和分享她喜欢的书籍。由于
小W是住校生,因此只有周末才能回家,而每次需要携带的书籍往往很多,所以她只能将
近期希望阅读或者分享的书籍分成几批携带到学校。
小W为了方便分批携带书籍,连夜将她的所有书籍测量出了它们各自的体积Ai,而这
也耗费了她的所有精力,因为测量书籍的体积是很累的。
不过小L在休息之前,将她剩下的工作委托给了她的学长小L。现在小L知道的信息极
其有限,只知道小W近期希望阅读或者分享的书籍总共有n本。
为了完成小W的委托,小L通过多方的询问才还原出小W书包的大致形状以及大小,
小L在几个小时的计算之后终于得到了小W书包所能容纳书籍的最大体积V。
因为长时间的高强度工作已经将小L累趴,无法继续完成小W委托给他的任务,作为
他学弟(学妹)的你们不忍心看到小L被小W痛骂,所以决定帮他完成剩下的任务。
现在你们需要计算最少要分成几批才能将她想携带的所有书籍都带到学校,而你们辛
勤的劳动也不是没有收获的,小L将根据你们帮他完成任务的多少给你们奖励——分数。

Input

第一行两个整数n和V,含义见上文。
接下来n行,第i行为Ai,含义见上文。

Output

仅一行,一个整数,表示你给出的答案。

Sample Input

4 10
5
6
3
7

Sample Output

3

HINT

类似于背包,因为有两个条件限制:最小次数与最小容量;故要开两个数组

f[i]表示当前状态下的需要用的最小次数
v[i]表示当前状态下的需要装的最小体积

当前装的容量要越小越好,这样可以保证后面可以装更大的体积,从而保证最小次数

code:

#include<bits/stdc++.h>
using namespace std;
int f[1<<23],v[1<<23];
int n,m;
int a[100010];
inline int read(){
     int x=0,f=1;
     char ch=getchar();
     while(ch<'0'||ch>'9'){
         if(ch=='-')
             f=-1;
         ch=getchar();
     }
     while(ch>='0'&&ch<='9'){
         x=(x<<1)+(x<<3)+(ch^48);
         ch=getchar();
     }
     return x*f;
}

inline void write(int x){
     char F[200];
     int tmp=x>0?x:-x ;
     if(x<0)putchar('-') ;
     int cnt=0 ;
        while(tmp>0)
        {
            F[cnt++]=tmp%10+'0';
            tmp/=10;
        }
        while(cnt>0)putchar(F[--cnt]) ;
}
main()
{
    //f[i]表示当前状态下的需要用的最小次数
    //v[i]表示当前状态下的需要装的最小体积 
    memset(f,63,sizeof(f));//最后次数要最小,所以初始最大 
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        f[1<<i-1]=1;//初始化 刚开始放任意一个值都为 1 
        v[1<<i-1]=a[i];//最开始容量为取得任意一个值 
    }
    for(int i=1;i<=(1<<n)-1;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i&1<<j-1)//若当前的j被取了则进行转移 
            {
                if(v[i^1<<j-1]+a[j]<=m)
                //上一个体积加上当前的体积小于等于总体积 
                {
                    if(f[i^1<<j-1]<f[i])
                    //若上一个最小次数比总共次数小,那么可行 
                    {
                         f[i]=f[i^1<<j-1];//更新 
                         v[i]=v[i^1<<j-1]+a[j];//体积也更新 
                    }
                    if(f[i^1<<j-1]==f[i])
                    {
                        //若次数与总和相等 
                        v[i]=min(v[i],v[i^1<<j-1]+a[j]);
                        //则体积取越小越好 
                        //这样可以保证剩余容量很大,则次数就小 
                    }
                }
                else //上一个体积加上当前的体积大于总体积 
                {
                    if(f[i^1<<j-1]+1<f[i])
                    {
                     
                        f[i]=f[i^1<<j-1]+1;//就要再来一组,装上当前体积
                        v[i]=a[j];
                        //当前状态的体积也要更新
                        //相当于开了一个背包来装另一个    
                    }
                }
            }
        }
    }
    cout<<f[(1<<n)-1];//枚举完所有状态求其最小次数 
    return 0;
}
原文地址:https://www.cnblogs.com/nlyzl/p/11301792.html