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


    题目链接

    题意(搬运自luogu):给出(n)个物品,体积为(w_i),现把其分成若干组,要求每组总体积$ le W$,问最小分组。
    ( $ n le 18 $, $ 1 le W le 1 imes 10^8 $,(1le 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;
    }
    
  • 相关阅读:
    git分支合并
    php错误处理
    php面试全套
    php面试的那些“黑话”
    快速在命令窗口打开当前路径
    @Autowired注解的使用方法
    jsp页面获取表单的值
    jsp打印九九乘法表
    Google hack
    java中的集合collection
  • 原文地址:https://www.cnblogs.com/fmj123/p/Luogu3052.html
Copyright © 2020-2023  润新知