码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2020-07-28 14:39:38      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:while   main   根据   ble   pair   cookie   通过   pac   blank   

题目链接: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

标签:while   main   根据   ble   pair   cookie   通过   pac   blank   

原文地址:https://www.cnblogs.com/randy-lo/p/13390342.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!