• 免费的糖果


    题目描述

    Petra和Jan分n个糖果,每个人轮流拿,一次只能拿一个,抽签决定谁先开始拿

    每个糖果有两个值x,y, 如果Petra拿了会获得值x, Jan拿了会获得值y。

    Petra每次都选择对自己价值最大的(x最大)拿,如果有多个x相同大,选择y值最小的

    Jan选择的策略是,要让自己最终获得的总价值最大, 并且在这个的前提下,要让Petra的值也尽量大。

    问最终他们获得的价值各是多少?

    输入

    第一行一个整数:测试用例的个数T(T<=100).

    之后每行:

    一个整数n(1<=n<=1000) :糖果的个数

    一行一个字符串,Petra或者是Jan:谁先选

    输出

    N行,每行两个数pi和ji(0<=xi,yi<=1000):第i个糖果的x和y每个测试用例输出一行两个数,Petra得到的x价值和Jan得到的y价值。

    样例输入

    3
    4
    Petra
    100 80
    70 80
    50 80
    30 50
    4
    Petra
    10 1
    1 10
    6 6
    4 4
    7
    Jan
    4 1
    3 1
    2 1
    1 1
    1 2
    1 3
    1 4

    样例输出

    170 130
    14 16
    9 1

    知识盲区,作为大一菜鸟,目前还不会

    借鉴借鉴别人的想法,学习学习

    思路
    这题的思维很巧妙


    先只考虑Petra拿糖的情况,他的策略是贪心的,排序一下,可以知道他一定是从按照顺序依次选择下去的
    看样例:
    Jan
    4 1
    3 1
    2 1
    1 1
    1 2
    1 3
    1 4
    这个样例已经按照Petra的贪心策略排序好了,第一个被Jan拿先拿了,第二个一定会被Petra拿去。
    接下来,如果Jan选择第3个,那么Petra就会拿第4个,如果Jam选除了第3个以外的任意一个,Petra都会拿走第3个。
    所以,Jan每一次的选择策略是,要不要把Petra下一次要拿的那个给“抢过来”!

    可以发现假设第一次是Jan开始拿(如果第一次是Petra拿,那么就从第二次开始算)
    前1个,Jan最多可以抢1个
    前2个,最多可以抢1个(如果拿了第1个,第2个一定会给Petra拿走,如果不拿第1个,那第1个就被Petra拿走了, Jan怎么也不可能拿走2个)
    前3个, 最多可以抢2个
    前4个,最多可以抢2个
    前5个,最多可以抢3个
    ...(以下省略)
    规律是,前i个,最多可以抢(i+1)/2个

    所以,我们可以用状态f(i, j),表示前i个,抢j个的时候,自己的最大值

    f(i, j) = max{ f(i-1, j), f(i-1,j-1) + y(i) |  当f(i-1, j-1)状态可达时);

    另外,题目要有一个限制:在Jan最大价值的情况,让Petra的价值也最大。

    那么,sum = x1+x2+x3+...xn, sum是所有糖果对Petra的价值之和

    每当Jan抢了一个的时候,Petra的sum就会减少xi, 我们要让所有减少的xi之和最少,
    这样,可以把物品x值看作是花费, y值看作是价值,目标是让Jan拿最大价值的情况下,花费最少
    那么我们可以再维护一个数组cost(i, j)即可

     1 #include 
     2 #include 
     3 #include 
     4 #include 
     5 #include 
     6 #include 
     7 #include 
     8 using namespace std;
     9  
    10 typedef long long int64;
    11 const int INF = 0x3f3f3f3f;
    12 const double PI  = acos(-1.0);
    13  
    14 const int MAXN = 1010;
    15 struct Node{
    16     int x, y;
    17     bool operator < (const Node& rhs) const {
    18         if (x != rhs.x) return x > rhs.x;
    19         return y < rhs.y;
    20     }
    21 }arr[MAXN];
    22  
    23 int n;
    24 int f[MAXN][MAXN/2];
    25 int cost[MAXN][MAXN/2];
    26 char name[10];
    27  
    28 int main(){
    29     
    30     int nCase;
    31     scanf("%d", &nCase);
    32     while (nCase--) {
    33         scanf("%d", &n);
    34  
    35         scanf("%s", name);
    36         int sum = 0;
    37         for (int i = 1; i <= n; ++i) {
    38             scanf("%d%d", &arr[i].x, &arr[i].y);
    39             sum += arr[i].x;
    40         }
    41  
    42         sort(arr+1, arr+1+n);
    43  
    44         memset(f, 0, sizeof(f));
    45         memset(cost, 0, sizeof(cost));
    46  
    47         int cur = 0;
    48         for (int i = (name[0]=='P'?2:1); i <= n; ++i) {
    49             ++cur;
    50             for (int j = 1; j <= (cur+1)/2; ++j) {
    51                 int& ans = f[i][j] = f[i-1][j];
    52                 cost[i][j] = cost[i-1][j];
    53  
    54                 if (j==1 || f[i-1][j-1]) { 
    55                     int tmp = f[i-1][j-1] + arr[i].y;
    56                     if (tmp > ans) {
    57                         ans = tmp;
    58                         cost[i][j] = cost[i-1][j-1] + arr[i].x;
    59                     } else if (tmp == ans) {
    60                         cost[i][j] = min(cost[i][j], cost[i-1][j-1]+arr[i].x);
    61                     }
    62                 }
    63             }
    64         }
    65         printf("%d %d
    ", sum-cost[n][(cur+1)/2], f[n][(cur+1)/2]);
    66     }
    67     return 0;
    68 }

    题意:Petra和Jan两个人,有一堆糖果,其中一个人先选,每个糖果有p,j值,代表Petra和Jan取得糖果后的开心值。Petra取糖果的策略是选择P尽量大,J尽量小的糖果,Jan的策略是保证自己最终快乐值尽量大并且Petra的快乐值也尽量大,问最终两个人的快乐值分别是多少。

    思路:Petra的策略满足贪心,所以先把糖果按P在按J排序,然后去取,就看Jan会取哪些糖果了,那么每个糖果就可以看成是Jan取不取,但是要注意,由于Petra是一定会往后一个取,所以Jan取糖果的时候是有一定的限制的,该限制为:假如都是Jan先取,1个糖果,Jan可以拿到,2个糖果,Jan可以拿其中一个,3个糖果Jan可以拿其中两个,4个糖果也是其中两个,以此类推,Jan能拿的糖果数为(i + 1)/2。因此dp[i][j]表示Jan在前i个糖果中拿了j个,j <= (i + 1)/2.

    状态转移方程为:如果不取 dp[i][j] = dp[i - 1][j] 如果取dp[i][j] = dp[i - 1][j - 1] + c[i].j;由于还要保证Petra尽量大,所以转移过程中记录被Jan取走糖果的p总和作为cost,然后Petra最后的快乐值为sump - cost;所以cost要尽量小,在状态转移过程中维护即可。

    题目可能是从Petra先取,不过这个稍微处理一下,假如Petra先取,就把第一个糖果给他,剩下的糖果在去dp。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <algorithm>
     4 #define min(a,b) ((a)<(b)?(a):(b))
     5 using namespace std;
     6  
     7 const int N = 1005;
     8 int t, n, dp[N][N], cost[N][N], sum;
     9 char name[10];
    10 struct Candy {
    11     int p, j;
    12 } c[N];
    13  
    14 bool cmp(Candy a, Candy b) {
    15     if (a.p != b.p)
    16         return a.p > b.p;
    17     return a.j < b.j;
    18 }
    19  
    20 void solve() {
    21     int bo = 0;
    22     if (name[0] == 'P')
    23         bo = 1;
    24     memset(dp, 0, sizeof(dp));
    25     memset(cost, 0, sizeof(cost));
    26     for (int i = 1; i <= n - bo; i++) {
    27         for (int j = 1; j <= (i + 1)/2; j++) {
    28             if (dp[i - 1][j] > dp[i - 1][j - 1] + c[i + bo].j) {
    29                 dp[i][j] = dp[i - 1][j];
    30                 cost[i][j] = cost[i - 1][j];
    31             }
    32             else if (dp[i - 1][j] == dp[i - 1][j - 1] + c[i + bo].j) {
    33                 dp[i][j] = dp[i - 1][j];
    34                 cost[i][j] = min(cost[i - 1][j], cost[i - 1][j - 1] + c[i + bo].p);
    35             }
    36             else {
    37                 dp[i][j] = dp[i - 1][j - 1] + c[i + bo].j;
    38                 cost[i][j] = cost[i - 1][j - 1] + c[i + bo].p;
    39             }
    40         }
    41     }
    42     printf("%d %d
    ", sum - cost[n - bo][(n - bo + 1) / 2], dp[n - bo][(n - bo + 1) / 2]);
    43 }
    44  
    45 int main() {
    46     scanf("%d", &t);
    47     while (t--) {
    48         scanf("%d%s", &n, name);
    49         sum = 0;
    50         for (int i = 1; i <= n; i++) {
    51             scanf("%d%d", &c[i].p, &c[i].j);
    52             sum += c[i].p;
    53         }
    54         sort(c + 1, c + n + 1, cmp);
    55         solve();
    56     }
    57     return 0;
    58 }

    思路:我们可以知道Petra直接贪心选取即可。所以对糖果的p进行排序,就看Jan会取哪些糖果了,那么每个糖果就可以看成是Jan取不取,但是要注意,由于Petra是一定会取一个剩下的p最大的,所以Jan取糖果的时候是有一定的限制的,限制为:如果Jan先取,1个糖果,Jan可以拿到一个;2个糖果,Jan可以拿其中一个;3个糖果Jan可以拿其中2个;4个糖果也是2个...以此类推,Jan能拿的糖果数为(i + 1)/2。因此dp[i][j]表示Jan在前i个糖果中拿了j个,j <= (i + 1)/2.注意有先后手问题,我们只需考虑Jan先拿,如果Petra先拿就直接从第二个开始变成Jan先手。

    转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-1]+a[i].y) 取或不取

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 typedef long long ll;
     7 const int maxn = 1e3+5;
     8 char str[maxn];
     9 struct node
    10 {
    11     int x, y;
    12     bool operator < (const node &a) const
    13     {
    14         if(x == a.x)
    15             return y < a.y;
    16         else
    17             return x > a.x;
    18     }
    19 }a[maxn];
    20 int dp[maxn][maxn], cost[maxn][maxn];
    21  
    22 int main(void)
    23 {
    24     int t, n;
    25     cin >> t;
    26     while(t--)
    27     {
    28         scanf("%d", &n);
    29         scanf(" %s", str);
    30         int s = 1, sum = 0;
    31         if(str[0] == 'P') s = 2;
    32         for(int i = 1; i <= n; i++)
    33             scanf("%d%d", &a[i].x, &a[i].y), sum += a[i].x;
    34         sort(a+1, a+1+n);
    35         int cur = 0;
    36         memset(dp, 0, sizeof(dp));
    37         for(int i = s; i <= n; i++)
    38         {
    39             cur++;
    40             for(int j = 1; j <= (cur+1)/2; j++)
    41             {
    42                 dp[i][j] = dp[i-1][j];
    43                 cost[i][j] = cost[i-1][j];
    44                 if(dp[i-1][j-1]+a[i].y > dp[i][j])
    45                 {
    46                     dp[i][j] = dp[i-1][j-1]+a[i].y;
    47                     cost[i][j] = cost[i-1][j-1]+a[i].x;
    48                 }
    49                 else if(dp[i-1][j-1]+a[i].y == dp[i][j])
    50                     cost[i][j] = min(cost[i][j], cost[i-1][j-1]+a[i].x);
    51             }
    52         }
    53         printf("%d %d
    ", sum-cost[n][(cur+1)/2], dp[n][(cur+1)/2]);
    54     }
    55     return 0;
    56 }
  • 相关阅读:
    codevs 1432 总数统计
    codevs3500 快速幂入门题解
    #163. 【清华集训2015】新式计算机
    2989:糖果
    191:钉子和小球
    java 删除所有HTML工具类
    DateTools时间插件
    新的开始
    springBoot---端口,路径数据配置
    springBoot---多个配置文件读取
  • 原文地址:https://www.cnblogs.com/jiamian/p/10646339.html
Copyright © 2020-2023  润新知