【CodeVS 1014】装箱问题

题目描述

有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。

要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

分析

思路1:判定性dp

f[i][j] 表示用了前n个物品,能否达到容量j。
然后对 f[n][j]=1 取最大的 j 就可以了。

思路2:偏离搜索

我们有种很好的思想:贪心。
先排序,然后每次尽可能取最大的。
但是这样是错误的。

我们考虑调整。
我们坚定几乎都是取最大的。
所以使用偏离搜索:只有限定次数能不取最大的。

结合一个剪枝:若之后的sum+当前的s<=v,则直接取完。

#include 
#include 
#include 
using namespace std;

#define rep(i,a,b) for (int i=(a);i<=(b);i++)

const int N=32;
const int D=5;

int v,n;
int a[N];

int sum[N];
int res;

inline int rd(void)
{
    int x=0,f=1; char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

void DFS(int w,int del,int s)
{
    if (s+sum[w]<=v)
    {
        res=max(res,s+sum[w]);
        return;
    }
    if (s+a[w]<=v)
    {
        DFS(w-1,del,s+a[w]);
        if (del1,del+1,s);
    }
    else DFS(w-1,del,s);
}

int main(void)
{
    freopen("codevs1014.in","r",stdin);
    freopen("codevs1014.out","w",stdout);

    v=rd(),n=rd();
    rep(i,1,n) a[i]=rd();
    sort(a+1,a+n+1);

    rep(i,1,n)
        sum[i]=sum[i-1]+a[i];
    DFS(n,0,0);
    printf("%d\n",v-res);

    return 0;
}

思路3:随机化

我们有种很好的思想:贪心。
先排序,然后每次尽可能取最大的。
但是这样是错误的。

我们考虑不排序了。
多次随机序列,然后能取就取。
随机1000000次即可。

#include 
#include 
#include 
#include 
using namespace std;

#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define per(i,a,b) for (int i=(a);i>=(b);i--)

const int N=32;
const int T=1000000;
const int MAX=INT_MAX;

int v,n;
int a[N];

int res;

inline int rd(void)
{
    int x=0,f=1; char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

int Calc(void)
{
    int nv=0;
    rep(i,1,n)
        if (nv+a[i]<=v)
            nv+=a[i];
    return v-nv;
}

int main(void)
{
//  freopen("codevs1014.in","r",stdin);
//  freopen("codevs1014.out","w",stdout);

    v=rd(),n=rd();
    rep(i,1,n) a[i]=rd();

    res=MAX;
    rep(tms,1,T)
    {
        random_shuffle(a+1,a+n+1);
        int t=Calc(); res=min(res,t);
    }
    printf("%d\n",res);

    return 0;
}

小结

(1)伪判定性问题
我们称“是不是”这种问题为判定性问题。
而很多最值问题都可以通过判定性问题来表述,所以称这类问题为伪判定性的。
总之:许多判定性问题可以转化为最值问题

(2)骗分的思路
骗分要有思路才行。
常规的骗分被称为“老实的骗分”,常见dfs求解,再结合两个常见的剪枝:①ans>=sum ②now+rest<=v
但是,有时候我们假如能想到一些错误的贪心,那么就可以诞生一些很棒的方法。常见方法1:偏离搜索。常见方法2:随机排列顺序,这种方法可用于最大团的求解。
总之,要活学活用,具体题目还是要具体分析。

你可能感兴趣的:(偏离搜索,随机化,贪心,背包dp,判定性dp)