《算法竞赛进阶指南》0x51线性DP Cookies

Posted randy-lo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法竞赛进阶指南》0x51线性DP Cookies相关的知识,希望对你有一定的参考价值。

题目链接:https://www.acwing.com/problem/content/279/

题目给定一个长度为n的序列g,和一个数m,要求将m分成n份,设定为数列a,使得数列g与数列a的乘积最小。根据排序不不等式,在g是升序的情况下,a是降序才会使得结果最小。所以对g进行降序排序之后,题意中的相对大小a[i]应该是升序的,所以此时每人分得的应该是降序的。

给第i个定数量的时候要知道前面有多少个一样的,阶段是分配完第i个人的量,前i个人一共用了j,若是每个人的数量都>1的则可以等价的转移到另一个[i,j-i]的等价集合,如果是1,则向前搜索长度连续的都是1的最小的目标值。最终通过回溯查看转移的方式,对结果进行保存。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int,int> PII;
const int maxn = 32;
const int maxm = 5050;
int n,m;
PII g[maxn];
int s[maxn];
int ans[maxn];
int f[maxn][maxm];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>g[i].first;
        g[i].second=i;
    }
    
    sort(g+1,g+n+1);
    reverse(g+1,g+n+1); 
    memset(f,0x3f,sizeof(f));
    
    for(int i=1;i<=n;i++)s[i]=s[i-1]+g[i].first;
    
    f[0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(i>j)continue;
            f[i][j]=f[i][j-i];
            for(int k=1;k<=i;k++){//枚举有多少个连续1 
                f[i][j]=min(f[i][j],f[i-k][j-k]+(s[i]-s[i-k])*(i-k));
            }
        }
    
    cout<<f[n][m]<<endl;
    
    int i=n,j=m,h=0;
    while(i){
        if(f[i][j]==f[i][j-i])j-=i,h++;
        else{
            for(int k=1;k<=i;k++)//枚举有多少个连续1
                if(f[i-k][j-k]+(s[i]-s[i-k])*(i-k) == f[i][j]){
                    for(int u=i-k+1;u<=i;u++)ans[g[u].second]=h+1; 
                    i-=k,j-=k;
                    break;
                } 
        }
    }
    for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
    cout<<endl;
    return 0;
}

 

以上是关于《算法竞赛进阶指南》0x51线性DP Cookies的主要内容,如果未能解决你的问题,请参考以下文章

《算法竞赛进阶指南》0x51线性DP 照相馆排列

《算法竞赛进阶指南》0x51线性DP 传纸条

《算法竞赛进阶指南》0x56状态压缩DP AcWing529 宝藏

《算法竞赛进阶指南》0x59单调队列优化DP AcWing 299裁剪序列

《算法竞赛进阶指南》0x35高斯消元与线性空间 异或空间

《算法竞赛进阶指南》0x57倍增优化DP AcWing293 开车旅行