神题
先按贪婪值大到小排序,根据贪心的思想g[i]越大a[i]也越大(这个微扰可以证,给个提示,a>b且c<d 则 (a-b)(c-d)<0 则 ac+bd<ad+bc)
DP有四个维吧,当前枚举到的位置,已经拿的饼数,最后一个人拿了多少饼以及它的a值是多少。
这样稳M稳T,由于a是有单调性,而且区间中饼同时多拿相对顺序不变a值也不变,于是乎我们玄学的把后两维删掉,假设当前位置只拿1个饼,有k个和它相等,一共拿了j个饼,这样可以得到转移的方程:f[i][j]=min(Σ(k<i) f[k][j-i+k]+k*Σ(p=k+1~n)g[p])
而全部人多拿一个饼对于答案没有影响所以f[i][j]也就可以继承f[i][j-i]
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct node{int g,id;}a[40]; bool cmp(node n1,node n2){return n1.g>n2.g;} int f[40][5100]; struct toback { int A,B; toback(){} toback(int a,int b){A=a,B=b;} }zz[40][5100]; int as[40]; int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i].g), a[i].id=i; sort(a+1,a+n+1,cmp); memset(f,63,sizeof(f));f[0][0]=0; for(int i=1;i<=n;i++) for(int j=i;j<=m;j++) { f[i][j]=f[i][j-i];//1~i ++; zz[i][j]=toback(i,j-i); for(int k=0;k<i;k++)//多少个和i一样 (包括i) { int sum=0; for(int p=k+1;p<=i;p++)sum+=a[p].g; sum*=k; if(f[i][j]>f[k][j-(i-k)]+sum) { f[i][j]=f[k][j-(i-k)]+sum; zz[i][j]=toback(k,j-(i-k)); } } } printf("%d ",f[n][m]); int x=n,y=m; memset(as,0,sizeof(as)); while(x!=0||y!=0) { int tx=zz[x][y].A,ty=zz[x][y].B; if(tx==x) { for(int j=1;j<=tx;j++)as[a[j].id]++; } else { for(int j=tx+1;j<=x;j++)as[a[j].id]++; } x=tx;y=ty; } for(int i=1;i<n;i++)printf("%d ",as[i]); printf("%d ",as[n]); return 0; }