http://acm.hdu.edu.cn/showproblem.php?pid=6011
题意:共有n种字符,每种字符有一个val和一个cnt,代表这个字符的价值和数量。可以制造的总价值是:第一个字符的权值*1+第二个字符的权值*2+第三个字符的权值*3+……。问最大的总价值可以是多少。
思路:首先可以确定价值越大的是放在越后,因为后面的位权比较大。考虑到价值有负数,因为不确定负数是否要放上去,所以需要枚举这些负数。
首先输入的时候记录价值为负的个数negnum。将价值从小到大排序,然后枚举1~negnum+1,用一个beg记录当前枚举哪种字符,还有tol记录当前种类的字符用了多少次了。如果当前种类的字符数用完了,就要将beg+1了,这样一直枚举下去,取最优。至于枚举到negnum+1的原因,是因为还要算一个负数都不取的情况会不会更优。计算的话可以通过等差数列求和公式(一个一个枚举怕超时,实际上看别人没超时)。至于在比赛时候WA的原因,因为粗心。。忘了只有枚举到的种类是beg的时候才要减去tol,其他不用。(比赛的时候所有都减去了tol,血崩)。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #include <iostream> 6 using namespace std; 7 #define N 1010 8 #define M 200010 9 #define INF 0x3f3f3f3f 10 typedef long long LL; 11 struct node { 12 int val, cnt; 13 } p[30]; 14 15 bool cmp(const node &a, const node &b) { return a.val < b.val; } 16 17 LL cal(int val, int cnt, int now) { // 等差数列 18 int n = cnt + now; 19 LL ans = val * n + (n * (n - 1)) / 2 * val; 20 ans -= val * now + (now * (now - 1)) / 2 * val; 21 return ans; 22 } 23 24 int main() { 25 int t; 26 scanf("%d", &t); 27 while(t--) { 28 int n; 29 scanf("%d", &n); 30 int negnum = 0; 31 for(int i = 1; i <= n; i++) { 32 scanf("%d%d", &p[i].val, &p[i].cnt); 33 if(p[i].val < 0) negnum += p[i].cnt; 34 } 35 sort(p + 1, p + 1 + n, cmp); 36 LL ans = 0, tmp = 0; int now = 0; 37 int beg = 1, tol = 0; 38 for(int i = 1; i <= negnum + 1; i++) { // 因为要看所有正数的情况,所以需要+1 39 now = 0; tmp = 0; 40 for(int k = beg; k <= n; k++) { 41 if(k == beg) tmp += cal(p[k].val, p[k].cnt - tol, now); 42 else tmp += cal(p[k].val, p[k].cnt, now); 43 if(k == beg) now += p[k].cnt - tol; 44 else now += p[k].cnt; 45 } 46 if(ans < tmp) ans = tmp; 47 tol++; 48 if(tol == p[beg].cnt) { beg++; tol = 0; } 49 } 50 printf("%I64d ", ans); 51 } 52 return 0; 53 }