UVALive 4945 Free Goodies

题目大意:n个糖果,两个人PetraheJan,每个糖果对于这两个人都有一定的val值,Petra每次选对于自己val值最大的那个,如果一样,就选Jan的val值小的,Jan想让自己选的糖果的val值之和最大,如果有多种路径,那么就选使Petra最后的val最大的那种(真是好基友,好丽友。。 = =),告诉你先手,轮着来。问你最后他们两个的分别得val值之和。

思路:(1)DP做法:设d[ i ][ j ] 为Jan在前i个糖果中选j个能获得的最大值(Jan为先手),而相同情况Petra的分数最大,状态转移方程为:d[ i ][ j ] = max((d[ i -1 ][ j ].p + good[ i ].p,d[ i - 1][ j ].j ),(d[ i - 1][ j ].p ,d[ i - 1][ j ].j + good[ i ].p)),这里要注意 j 的边界,只能到一半。

我自己是没想到。。 看了别人的博客后才知道的。。 感觉对这种博弈问题不是很懂,先慢慢积累着吧。。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 1111 ;

char name[11] ;

struct Good
{
    int p,j;
    bool operator < (const Good &tmp) const
    {
        if(p==tmp.p) return j<tmp.j;
        else return p>tmp.p;
    }
} good[MAXN];

struct Node
{
    int p,j;
    Node(){}
    Node(int a,int b) : p(a),j(b) {}
    bool operator < (const Node &tmp) const
    {
        if(j == tmp.j) return p<tmp.p;
        else return j<tmp.j;
    }
}d[MAXN][MAXN],ans;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",name);
        for(int i = 1;i<=n;i++)
            scanf("%d%d",&good[i].p,&good[i].j);
        sort(good+1,good+1+n);
        for(int i = 0;i<=n;i++)
            d[0][i].p = d[0][i].j = -INF;
        if(name[0]=='P')
        {
            d[0][0].p = good[1].p;
            d[0][0].j = 0;
            for(int i = 1;i<n;i++)
                good[i] = good[i+1];
            n--;
        }
        else d[0][0].p = d[0][0].j = 0;
        ans = d[0][0];
        for(int i = 1;i<=n;i++)
        {
            for(int j = 0;j<=(i+1)/2;j++)
            {
                d[i][j] =d[i-1][j];
                if(j>=1)
                    d[i][j] = max(d[i][j],Node(d[i-1][j-1].p,d[i-1][j-1].j+good[i].j));
                d[i][j] = max(d[i][j],Node(d[i-1][j].p+good[i].p,d[i-1][j].j));
                if(j==(n+1)/2)
                    ans = max(ans,d[i][j]);
                //printf("d[%d][%d] p = %d,j = %d\n",i,j,d[i][j].p,d[i][j].j);
            }
            for(int j = (i+1)/2+1;j<=n;j++)
                d[i][j].p = d[i][j].j = -INF;
        }
        printf("%d %d\n",ans.p,ans.j);
    }
    return 0;
}


(2)贪心。先按照Petra的顺序两人逐个选取,就会得到两组,一组是Petra的,一组是Jan的,然后再倒着来,对于每个Jan选取的糖果,对于当前点再往下找,去和本来Petra的糖果进行贪心,再交换,因为Jan可以随意取,他总可以拿到这些糖果。

其实我也是一知半解的,思维弱啊。。 如果大家谁能很清楚地解释其中的思想,欢迎和我交流。。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

#define MP make_pair

typedef long long lld;

const int INF = 0x0fffffff ;

const int MAXN = 1111 ;

struct Good
{
    int p,j;
    Good(){}
    Good(int a,int b) : p(a),j(b) {}
    bool operator < (const Good& tmp) const
    {
        if(p == tmp.p) return j < tmp.j;
        else return p > tmp.p;
    }
} good[MAXN];

char name[11];

int vis[MAXN];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",name);
        int a,b;
        for(int i = 1;i<=n;i++)
        {
            scanf("%d%d",&a,&b);
            good[i] = Good(a,b);
        }
        sort(good+1,good+1+n);
        int flag ;
        if(name[0]=='P')
            flag =0 ;
        else flag = 1;
        for(int i = 1;i<=n;i++)
        {
            if(flag==1)
            {
                vis[i] = 1;
            }
            else vis[i]=0;
            flag ^= 1;
        }
        for(int i = n;i>=1;i--)
        {
            if(vis[i])
            {
                vis[i] = 0;
                int tmp = i;
                for(int j = i+1;j<=n;j++)
                    if(!vis[j]&&good[j].j>=good[tmp].j)
                        tmp = j;
                vis[tmp] = 1;
            }
        }
        int val_p = 0,val_j = 0 ;
        for(int i = 1;i<=n;i++)
        {
            if(vis[i])
                val_j += good[i].j;
            else val_p += good[i].p;
        }
        printf("%d %d\n",val_p,val_j);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值