zoj 3905(dp+滾動數組)

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

1
6
1 6
7 10
6 11
12 18
15 5
2 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 1000000007
const 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 1000000007
const 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;
}



發佈了161 篇原創文章 · 獲贊 45 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章