Luogu P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper|DP

题目链接

题意(搬运自luogu):给出\(n\)个物品,体积为\(w_i\),现把其分成若干组,要求每组总体积$ \le W$,问最小分组。
( $ n \le 18 $, $ 1 \le W \le 1\times 10^8 $,\(1\le c \le w\))

看起来本题无从下手,所以我们可以先用\(dfs\),我们设\((x,y,z)\)的意义是当前选择状态为\(x\),当前分了\(y\)组,最后一组还剩\(z\)空间。这个状态中,我们并不需要考虑将一个物品放在前面的组中,因为这个等同于在该状态前面就放入了这个物品。则我们在每轮\(dfs\)中枚举放哪一个物品到最后一组(或新开一组)。

这种做法看起来不太好,我们尝试优化。

我们可以证明在\(x,y\)相同时,对于\(z\)\(z'\),若\(z>z'\)\((x,y,z)\)推出来的答案一定不会比\((x,y,z')\)更劣,那我们就不需要\(dfs\) \((x,y,z')\)的状态了。

因此,我们记录每个\((x,y)\)中的最小\(z\),若\(z'>z\)就不\(dfs (x,y,z')\)

与此同时,我们在\(dfs\)中也不需要记录\(z\)了。因为\(z\)一定是我们记下的最小的\(z\)

然而这样就过了???

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100],ans=2147483647;
int f[262147][20];//记录(x,y,z)中z的最小值,大于则不用再搜
void dfs(int x,int y)
{
    if (x==(1<<n)-1) ans=min(ans,y);//结束状态
	int z=f[x][y];
    for (int i=1;i<=n;i++)
    {
        if (!((x>>(i-1))&1))//当前位没放入任意一组
        {
            int newx=x+(1<<(i-1));
            if (z>=a[i])//放入当前组
            {
                if (z-a[i]>f[newx][y])//优化
                {
                    f[newx][y]=z-a[i];
                    dfs(newx,y);
                }
            }
            else//新开一组
            {
                if (m-a[i]>f[newx][y+1])//优化
                {
                    f[newx][y+1]=m-a[i];
                    dfs(newx,y+1);
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=0;i<(1<<n);i++)
      for (int j=0;j<=n;j++)
        f[i][j]=-1;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    dfs(0,0);
    cout<<ans<<endl;
    return 0;
}

原来只要把y按同样方法做就可以加速了

按上面的逻辑,显然\(x\)相同时,\(y\)越小越好,则我们可以再次优化。

贴上再次优化后代码

//除优化了y,其余相同
#include<bits/stdc++.h>
using namespace std;
int n,m,a[100],ans=2147483647;
int f[262147],f1[262147];//f1表示相同状态下最小的y
void dfs(int x)
{
	int y=f1[x];int z=f[x];
    if (x==(1<<n)-1) ans=min(ans,y);
    for (int i=1;i<=n;i++)
    {
        if (!((x>>(i-1))&1))
        {
            int newx=x+(1<<(i-1));
            if (z>=a[i])
            {
            	if (y<=f1[newx])
            	{
			      if (y<f1[newx]) f[newx]=-1;
            	  f1[newx]=y;
                  if (z-a[i]>f[newx])
                  {
                      f[newx]=z-a[i];
                      dfs(newx);
                  }
                }
            }
            else
            {
            	if (y+1<=f1[newx])
            	{
			      if (y+1<f1[newx]) f[newx]=-1;
            	  f1[newx]=y+1;
                  if (m-a[i]>f[newx])
                	{
                    	f[newx]=m-a[i];
                    	dfs(newx);
                	}
            	}
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=0;i<(1<<n);i++)
        f1[i]=2147483647,f[i]=-1;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    f[0]=-1;f1[0]=0;
    dfs(0);
    cout<<ans<<endl;
    return 0;
}
posted @ 2019-06-10 13:25  fmj_123  阅读(220)  评论(0编辑  收藏  举报