zoj3905 Cake

4 篇文章 0 订阅
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


题意:有n个球,两个人玩游戏,每次选出两个球,B会选出他认为球的价值比较大的,会挑走一个球,然后A会得到另一个球的价值,问怎样取能使A获得的价值最大。

思路:可以先按B所认为的价值从大到小排序,那么对于任意前m件物品,A可以得到m/2件,然后设dp[i][j]表示前i件物品A取出j件最多获得的价值,那么当循环到前第i件时有两种转移形式,一种是这一件不取,那么前i-1件就要取j件,否则就要取j-1件,即dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+c[i].a);


#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 0x7fffffff
#define maxn 810
struct node{
    int a,b;
}c[maxn];

bool cmp(node a,node b){
    return a.b>b.b;
}
int dp[maxn][maxn];

int main()
{
    int n,m,i,j,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%d%d",&c[i].a,&c[i].b);

        }
        sort(c+1,c+1+n,cmp);
        memset(dp,-1,sizeof(dp));
        dp[1][0]=0;
        for(i=2;i<=n;i++){
            dp[i][0]=0;
            for(j=1;j<=i/2;j++){
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+c[i].a);

            }
        }
        printf("%d\n",dp[n][n/2]);
    }
}


还有两种是贪心的解法,一个是从前往后贪心,还有一个是从后往前贪心,两者的本质是一样的。我们考虑从后往前贪心,因为不管A所选择的两个数是什么,都是B先选,所以我们可以先把n个数据按b为第一关键字从大到小排序,按a从大到小排序。那么不管我们取出哪两个数,B都是取排在前面的数,题目等价于有n个数,B按顺序取对其来说价值最大的数,A尽可能多的取得对它来说价值大的数。那么我们考虑B取法的规律,前1个数,B至少取1个,前3个数,B至少取2个,前5个数,B至少取3个....前n(n为奇数)个数,B至少取n/2+1个,所以我们可以推出前1个数必由B取,前3个数A至多取1个,前5个数,A至多取3个...那么我们便可进一步推出,后1个数,A至少取1个,后3个数,A至少取2个,后5个数,B至少取3个,那么我们可以从后往前每次把2个数放入set里,然后把set里最大的数取出,加上该值就行了。


代码一:(从后往前推)

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
#define inf 99999999
#define pi acos(-1.0)
#define maxn 1000
struct node1{
    int num;
}temp,temp1;
bool operator<(node1 c,node1 d){
    return c.num>d.num;
}
set<node1>myset;
set<node1>::iterator it;

struct node{
    int a,b;
}c[maxn];
bool cmp(node c,node d){
    return c.b>d.b;
}


int main()
{
    int n,m,i,j,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%d%d",&c[i].a,&c[i].b);
        }
        sort(c+1,c+1+n,cmp);
        if(n==2){
            printf("%d\n",c[2].a);
            continue;
        }
        int sum=c[n].a;
        myset.clear();
        for(i=n-1;i>=1;i--){
            node1 temp;
            temp.num=c[i].a;
            myset.insert(temp);
            if(i%2==0){
                it=myset.begin();
                sum+=(*it).num;
                myset.erase(*it);
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

/*
1
6
1 6
7 10
6 11
12 18
15 5
2 14
*/



代码二:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
#define inf 99999999
#define pi acos(-1.0)
#define maxn 1000
set<int>myset;
set<int>::iterator it;

struct node{
    int a,b;
}c[maxn];
bool cmp(node c,node d){
    return c.b>d.b;
}


int main()
{
    int n,m,i,j,T,sum1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        sum1=0;
        for(i=1;i<=n;i++){
            scanf("%d%d",&c[i].a,&c[i].b);
            sum1+=c[i].a;
        }
        sort(c+1,c+1+n,cmp);
        if(n==2){
            printf("%d\n",c[2].a);
            continue;
        }
        int sum=0;
        myset.clear();
        for(i=1;i<=n;i++){
            myset.insert(c[i].a);
            if(i%2==1){
                it=myset.begin();
                sum+=*it;
                myset.erase(it);
            }
        }
        printf("%d\n",sum1-sum);
    }
    return 0;
}

/*
1
6
1 6
7 10
6 11
12 18
15 5
2 14
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值