【SHOI2007/BZOJ1933】书柜的尺寸 dp

原题走这里
目前见过的DP中算是思维难度比较大的了,其主要原因在于我太菜了

本题问的是书柜总高度*书柜长度的最小值,这种情况下,我们应当将其中一个作为状态,另外一个作为代价,然后统计得到最小值。
于是,我们就可以把状态设为 d[i][j][k] d [ i ] [ j ] [ k ] ,表示前 i i 本书,三层书架长度分别为 j,k,sum[i]jk j , k , s u m [ i ] − j − k 的情况下,最小的总高度。

然而我们就会发现,在递推过程中,我们不仅需要知道前驱状态中最小的总高度,还要知道各个书架的高度,这样我们才能确定:放新书的时候,会不会增加该层书架的高度。因此,我们的DP无法进行。

那么如何消除各个书架高度对状态的影响呢?

我们可以把书按高度降序排序。
这样的话,每次往一个非空的层里放书,都不会改变书架的高度。
于是我们的DP得以进行:
每次枚举往哪个层里放书,假如当前层的长度为0,则最小总高度加上这本书的高度,然后每次取最小值即可。最后统计,求min,输出,AC。
具体细节见代码。

代码如下:

// luogu-judger-enable-o2
#include 
using namespace std;
int n,d[2110][2110],h[80],t[80],s[80],sum;
long long ret=1e18;
pair<int,int> a[80];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].first>>a[i].second;
        sum+=a[i].second;
    }
    sort(a+1,a+n+1);
    reverse(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        h[i]=a[i].first;
        t[i]=a[i].second;
        s[i]=s[i-1]+t[i];
    }
    memset(d,0x3f,sizeof(d));
    d[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(register int j=sum;j>=0;j--)
        {
            for(register int k=sum-j;k>=0;k--)
            {
                int t1=(j>=t[i]?d[j-t[i]][k]+(j==t[i]?h[i]:0):(int)(1e9));
                int t2=(k>=t[i]?d[j][k-t[i]]+(k==t[i]?h[i]:0):(int)(1e9));
                int t3=(s[i]-j-k>=t[i]?d[j][k]+(t[i]==s[i]-j-k?h[i]:0):int(1e9));
                d[j][k]=min(t3,min(t1,t2));
            }
        }
    }
    for(int i=1;i<=sum;i++)
    {
        for(int j=1;j<=sum;j++)
        {
            if(i+j1LL*max(sum-i-j,max(i,j))*d[i][j]);
        }
    }
    cout<return 0;
} 
/*
4
1 1
2 2
3 3
4 4
*/

你可能感兴趣的:(【SHOI2007/BZOJ1933】书柜的尺寸 dp)