也是一道线型动态规划的好题……
读入每个人的贪婪度之后,对其按照从大到小的顺序排序,定义状态f[i][j]为前i个人(排序后)分j个饼干的答案,那么答案为f[n][m],考虑状态转移方程。
1、若给第i个人的饼干数大于1 ,那么我们将这i个人的饼干数都减1(总共减n),他们的怨气值是不会改变的,因而这种情况下,f[i][j]=f[i][j-i].
2、若给第i个人的饼干数等于1,那么我们枚举一个k(0≤k<i),表示从k之后一直到i所有的人的饼干数都是1,那么f[i][j]=f[k][j-(i-k)]+k*∑g[c[p]] (k<p<=i).
我们先预处理出g数组的前缀和,即可实现O(n)的转移。
综上,我们在两种决策中取最优即可。另外,本题要求输出方案,我们只需在状态转移时记录每个状态的前驱即可。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 int n,m,f[40][5010],a[40][5010],b[40][5010]; 7 int s[50],ans[50]; 8 int g[50],c[50]; 9 bool cmp(int x,int y) { 10 return g[x]>g[y]; 11 } 12 void calc(int x,int y) { 13 if(!x) return ; 14 calc(a[x][y],b[x][y]); 15 if(a[x][y]==x) 16 for(int i=1;i<=x;i++) ans[c[i]]++; 17 else 18 for(int i=a[x][y]+1;i<=x;i++) ans[c[i]]=1; 19 } 20 int main() { 21 scanf("%d%d",&n,&m); 22 for(int i=1;i<=n;i++) { 23 scanf("%d",&g[i]); 24 c[i]=i; 25 } 26 sort(c+1,c+n+1,cmp); 27 memset(f,0x3f,sizeof(f)); 28 f[0][0]=0; 29 for(int i=1;i<=n;i++) s[i]=s[i-1]+g[c[i]]; 30 for(int i=1;i<=n;i++) 31 for(int j=i;j<=m;j++) { 32 f[i][j]=f[i][j-i]; 33 a[i][j]=i; 34 b[i][j]=j-i; 35 for(int k=0;k<i;k++) 36 if(f[i][j]>f[k][j-(i-k)]+k*(s[i]-s[k])) { 37 f[i][j]=f[k][j-(i-k)]+k*(s[i]-s[k]); 38 a[i][j]=k; 39 b[i][j]=j-(i-k); 40 } 41 } 42 printf("%d\n",f[n][m]); 43 calc(n,m); 44 for(int i=1;i<=n;i++) printf("%d ",ans[i]); 45 return 0; 46 }