http://acm.whu.edu.cn/land/problem/detail?problem_id=1538
还是给你石头n枚,每一枚石头有两个值a和b,每取一个石头,除了这块石头其余所有的石头的a就都减去这个石头的b,问你取了的石头的a的总和最大可以为多少?
解题思路:
把石头按照b从大到小排列,因为越大的越要倒数(放在后面)取,就越要排在数组前面。关于这一点,昨天晚上又仔细想了一遍。首先如果两个b相等,那么谁前谁后没关系,因为减去的都是b,通过dp可以取出其中a大的那种情况。然后就是一个01背包。
因为第k次取会影响到第k+1次取,所以想到从后往前取。先取倒数第1个,再取倒数第二个,这样得到状态转移方程:
dp[i][j] = max( dp[i-1][j], dp[i-1][j-1] + node[i].a - node[i].b * (j-1) )
dp[i][j] 表示前i个里面取j个时的a最大总和。
用前i-1个里面取j个时的最大值的情况,比较前i-1个里面取j-1个的最大值 加上把第i个放到倒数第j个取的情况。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <cstring>
#include <string>
#include <cmath>
#include <ctime>
#include <utility>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define FOR(a,b,c) for (int a=b,_c=c;a<=_c;a++)
#define FORD(a,b,c) for (int a=b;a>=c;a--)
#define REP(i,a) for(int i=0,_a=(a); i<_a; ++i)
#define REPD(i,a) for(int i=(a)-1; i>=0; --i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define sz(a) int(a.size())
#define reset(a,b) memset(a,b,sizeof(a))
#define oo 1000000007
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1010;
pii node[maxn];
int dp[maxn][maxn];
bool cmp(const pii &x, const pii &y)
{
return x.se > y.se;
}
int main()
{
// freopen("data.in", "r", stdin);
int n, ans;
while(scanf("%d", &n) != EOF && n != 0)
{
FOR(i, 1, n) scanf("%d%d", &node[i].fi, &node[i].se);
sort(node+1, node+n+1, cmp);
FOR(i, 0, n) dp[i][0] = 0;
FOR(i, 1, n)
FOR(j, 1, i)
{
dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + node[i].fi - node[i].se * (j-1));
}
ans = 0;
FOR(i, 1, n) ans = max(ans, dp[n][i]);
printf("%d\n", ans);
}
return 0;
}