《算法竞赛进阶指南》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的主要内容,如果未能解决你的问题,请参考以下文章
《算法竞赛进阶指南》0x56状态压缩DP AcWing529 宝藏