HDU 5406
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5406
题意:
给一堆苹果,每个苹果有重量和价值。两个人取苹果,对于一个人,只能取一个苹果j,相比上次取得苹果,j的重量要小,价值要大。
问最后两个人取的最多的苹果数量是多少。
思路:
感谢http://blog.csdn.net/l_ecry/article/details/47830927
先写解题思路,看懂的~
初始想法是dp,但是发现有两个人无法处理。实际上有两个人的时候通常设dp[i][j],分别表示第一个人状态为i,第二个人状态为j。
本题也是如此,设dp[i][j]为第一个人在i点,第二个人在j点时的最大值。
下面要决定如何去dp,也就是dp的顺序。
题解采取topo序dp。按照不同高度高的在前面、相同高度D值小的在前面排序后,建立topo图。然后在图上,如果当前点i的d值比后面点j的d值小,则i与j连一条边。
然而这样是会TLE的。
于是原作者加了两个个剪枝。
剪枝一:若当前j的d值和已经遍历到并且与i相连点的最小d值要大,则不在i与j之间连边。简单证明:假设当前点为j2,d值为d2,而已经遍历到d值最小的点j1,d值d1。若d1 < d2,则在遍历到j1时,j2肯定会与j1连一条边,这样得出来的结果肯定比i直接与j2相连要大。证毕。
剪枝二:假设第一个人站的点i<=第二个人站的点j,这样能减少很多无效转移。
第一次做非数组顺序的dp题,感觉关键是原数组顺序和dp顺序之间的转换,具体就是开两个数组互相指针指一指。
简直就是照着抄了一遍
源码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
#define inf (1000000050)
#define gmax(a,b) ((a) > (b) ? (a) : (b))
const int MAXN = 1000 + 50;
int dp[MAXN][MAXN], n;
vector<int>lin[MAXN];
struct Lv
{
int h, d;
}lv[MAXN];
bool cmp(Lv a, Lv b)
{
if(a.h == b.h) return a.d < b.d;
return a.h > b.h;
}
int q[MAXN], id[MAXN], tail;
int in[MAXN];
void topo()
{
memset(in, 0, sizeof(in));
tail = 0;
q[tail++] = 0;
id[0] = 0;
for(int i = 1 ; i <= n ; i++){
lin[0].push_back(i);
in[i]++;
for(int j = i + 1 ; j <= n ; j++){
if(lv[j].d >= lv[i].d){
lin[i].push_back(j);
in[j]++;
}
}
in[n + 1]++;
lin[i].push_back(n + 1);
}
for(int i = 0 ; i < tail ; i++){
for(int j = 0 ; j < (int)lin[i].size() ; j++){
int v = lin[i][j];
in[v]--;
if(in[v] == 0){
q[tail++] = v;
id[v] = tail - 1;
}
}
}
}
void DP()
{
for(int i = 0 ; i <= n + 1 ; i++)
for(int j = 0 ; j <= n + 1 ; j++)
dp[i][j] = -inf;
dp[0][0] = 0;
for(int i = 0 ; i <= n + 1 ; i++){
for(int j = i ; j <= n + 1 ; j++){
if(dp[i][j] < 0)
continue;
int u = q[i];
for(int k = 0 ; k < (int)lin[u].size() ; k++){
int x = id[lin[u][k]];
if(x == j)
dp[x][j] = gmax(dp[x][j], dp[i][j]);
else if(x < j)
dp[x][j] = gmax(dp[x][j], dp[i][j] + 1);
else if(x > j)
dp[j][x] = gmax(dp[j][x], dp[i][j] + 1);
}
}
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
for(int i = 1 ; i <= n ; i++)
scanf("%d%d", &lv[i].h, &lv[i].d);
sort(lv + 1, lv + n + 1, cmp);
for(int i = 0 ; i <= n + 1 ; i++)
lin[i].clear();
lv[0].h = 0, lv[0].d = 0;
lv[n+1].h = inf, lv[n+1].d = inf;
topo();
for(int i = 0 ; i <= n + 1 ; i++)
lin[i].clear();
for(int i = n ; i >= 0; i--){
int mm = inf + 5;
for(int j = i + 1 ; j <= n + 1 ; j++){
if(mm <= lv[j].d)
continue;
// if(i == 4)
// printf("j = %d, mm = %d, lv[j].d = %d\n", j, mm, lv[j].d);
if(lv[i].d <= lv[j].d){
lin[i].push_back(j);
mm = min(mm, lv[j].d);
}
}
}
// printf("lin\n");
// for(int i = 0 ; i <= n ; i++){
// printf("i = %d, h = %d, d = %d, j = ", i, lv[i].h, lv[i].d);
// for(int k = 0 ; k < (int)lin[i].size() ; k++)
// printf("%d ", lin[i][k]);
// printf("\n");
// }
DP();
// printf("dp\n");
// for(int i = 0 ; i <= n + 1; i++){
// for(int j = 0 ; j <= n + 1 ; j++)
// printf("%11d ", dp[i][j]);
// printf("\n");
// }
printf("%d\n", dp[n+1][n+1] - 1);
}
return 0;
}