zoj 3905(dp+滚动数组)

来源:互联网 发布:宜宾零距离网络论坛 编辑:程序博客网 时间:2024/05/03 09:17
Cake

Time Limit: 4 Seconds      Memory Limit: 65536 KB

Alice and Bob like eating cake very much. One day, Alice and Bob went to a bakery and bought many cakes.

Now we know that they have bought n cakes in the bakery. Both of them like delicious cakes, but they evaluate the cakes as different values. So they decided to divide those cakes by following method.

Alice and Bob do n / 2 steps, at each step, Alice choose 2 cakes, and Bob takes the cake that he evaluates it greater, and Alice take the rest cake.

Now Alice want to know the maximum sum of the value that she can get.

Input

The first line is an integer T which is the number of test cases.

For each test case, the first line is an integer n (1<=n<=800). Note that n is always an even integer.

In following n lines, each line contains two integers a[i] and b[i], where a[i] is the value of ith cake that Alice evaluates, and b[i] is the value of ith cake that Bob evaluates. (1<=a[i]b[i]<=1000000)

Note that a[1]a[2]..., a[n] are n distinct integers and b[1]b[2]..., b[n] are n distinct integers.

Output

For each test case, you need to output the maximum sum of the value that Alice can get in a line.

Sample Input

161 67 106 1112 1815 52 14

Sample Output

28

Author: HUA, Yiwei
Source: ZOJ Monthly, October 2015

月赛的时候又没做出来,好桑心。。

题意:
有n(n为偶数)块蛋糕要被平分给A和B,A和B对于每块蛋糕都有一个他们自己心中的价值。然后是这样平分的:A从中选出2块让B选,B肯定选他认为价值更高的那一块,而另一块就给A,重复上述操作直到被分完。求A可能得到的最大价值。

分析:
其实只要能定义好状态,那么递推方程就不难推出来。定义状态时还要排除一些干扰,比如我一开始总觉得要配对什么的。。
dp[i][j]表示前i个蛋糕中A选j个得到的最大和,方程由“取与不取第i个蛋糕"得出。
code:
#include<stdio.h>#include<string.h>#include<map>#include<algorithm>using namespace std;typedef long long ll;#define F first#define S second#define mod 1000000007const int INF = 1<<30;const int N = 810;int dp[N][N];pair<int,int> pr[N];bool cmp(pair<int,int> i, pair<int,int> j) {return i>j;}int main(){    int T, n;    scanf("%d", &T);    while(T--)    {        scanf("%d", &n);        for(int i=1; i<=n; i++)            scanf("%d%d", &pr[i].S,&pr[i].F);        sort(pr+1,pr+n+1,cmp); //知道递推方程怎么来的就知道为什么要排序了        memset(dp, 0, sizeof(dp));        for(int i=1; i<=n; i++)        {            for(int j=1; j<=i/2; j++)            {                dp[i][j] = max(dp[i-1][j],dp[i-1][j-1]+pr[i].S);            }        }        printf("%d\n", dp[n][n/2]);    }    return 0;}
可以看到每层状态都是由上一层状态转移过来的,所以这里可以采用滚动数组减少一维空间,只需重复利用一维来进行操作。可以看到如果是二维的话,转移的过程并不会影响到上一层,而如果是一维的话: dp[j] = max(dp[j],dp[j-1]+...); 这样其实是有点问题的,问题在于更新 j 这个状态时其实是覆盖了相当于二维的上一层的 j 状态,那么在更新 j+1 这个状态时就不是由原来的 j 这个状态转移来的了。所以我们可以将循环的顺序调转一下,这样就不会有所影响了。
code:
#include<stdio.h>#include<string.h>#include<map>#include<algorithm>using namespace std;typedef long long ll;#define F first#define S second#define mod 1000000007const int INF = 1<<30;const int N = 810;int dp[N];typedef pair<int,int> pii;pii pr[N];bool cmp(pii i, pii j) {return i>j;}int main(){    int T, n;    scanf("%d", &T);    while(T--)    {        scanf("%d", &n);        for(int i=1; i<=n; i++)            scanf("%d%d", &pr[i].S,&pr[i].F);        sort(pr+1,pr+n+1,cmp);        memset(dp, 0, sizeof(dp));        for(int i=1; i<=n; i++)        {            for(int j=i/2; j>=1; j--)            {                dp[j] = max(dp[j],dp[j-1]+pr[i].S);            }        }        printf("%d\n", dp[n/2]);    }    return 0;}



0 0
原创粉丝点击