Gym 100340A Cookies

https://codeforces.com/gym/100340/problem/A

题目

老人给n个小学生发m个饼干,每人最少一个,最后都发完了。这些小学生每个人都有一个贪心值g,如果有x个人的饼干比他多,那么他的不高兴指数就是$gx$

给出n,m,所有贪心值,输出不高兴指数的和的最小值,同时输出如何发饼干才能使小学生的不高兴指数的和最小。

题解

首先贪心,如果有两个小学生的贪心值分别为$a$和$b$,发的饼干分别为$c$和$d$,若$a<b,c<d$,那么$ac+bd>ad+bc$($a(c-d)-b(c-d)=(a-b)(c-d)>0$),所以贪心值大的就需要多发

将贪心值排序后,从左往右发的饼干递减,那么就相当于一层一层搭上去的。

就可以写转移了

$dp[i][j]$表示前$i$个人发$j$块饼干

就有两种转移

  • $dp[i][j]=dp[i][j-i]$
  • $dp[i][j]=dp[k][j-i]+k imes(s[i]-s[k])$

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<cassert>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
int n,m;
struct node {
	int a,id;
	bool operator<(const node &r) const {
		return a>r.a;
	}
} g[37];
int s[37];
int dp[37][5007], pi[37][5007], pj[37][5007];
int ans[37];
inline void prn(int i, int j) {
	if(i==-1) return;
	REPE(x,1,i) ans[g[x].id]++;
	prn(pi[i][j], pj[i][j]);
}
int main() {
#ifndef LOCAL
	freopen("cookies.in", "r", stdin);
	freopen("cookies.out", "w", stdout);
#endif
	memset(ans,0,sizeof ans);
	scanf("%d%d", &n, &m);
	REPE(i,1,n) scanf("%d", &g[i].a), g[i].id=i-1;
	sort(g+1,g+n+1);
	s[0]=0;
	REPE(i,1,n) s[i]=s[i-1]+g[i].a;
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0, pi[0][0]=-1, pj[0][0]=-1;
	REPE(i,1,n) REPE(j,i,m) {
		dp[i][j] = dp[i][j-i], pi[i][j]=i, pj[i][j]=j-i;
		REP(k,0,i) {
			int nd = dp[k][j-i] + k*(s[i]-s[k]);
			if(nd<dp[i][j]) {
				dp[i][j]=nd, pi[i][j]=k, pj[i][j]=j-i;
			}
		}

	}
	printf("%d
", dp[n][m]);
	prn(n, m);
	REP(i,0,n) {
		if(i) putchar(' ');
		printf("%d", ans[i]);
	}
}
原文地址:https://www.cnblogs.com/sahdsg/p/12416466.html