• CF1251 的题解


    CF1251 的题解

     

             没有错,本小菜鸡又腆着个波脸来写题解了,菜还理直气壮。

    A题

            

             题面:说有一个键盘坏了,然后坏掉的键按一次会出现两个相应的字母,好的键是按一次只会出现一个相应的字母。

      做法:需要判断一个字母连续出现的次数,如果为奇数那么说明这个键是好的。

      水题,没错我又觉得这是水题了

    B题

             题面:给出n个01字符串,然后意思是可以任意交换01的位置,哪怕不是一个字符串的也可以交换。问最多可以转化出多少个回文串。

             做法:首先我们可以想到对于长度为奇数的01字符串来说,他必是一个回文串(您要是能举出反例,我就是粪海狂蛆)。 对于长度为偶数的01字符串来说,如果不是回文串那么说明此时01的个数都是奇数。我们只需要任意换掉一个0或者1就可组成一个回文串了。

            

             我们数出不是回文串的01串的个数尽心两两配对(一个拿1一个拿0)。

    如果个数为偶数那么就是最好的情况,答案就是n;如果为奇数,我们就需要考虑有没有长度为奇数的01串,我们和中间那个位置的字符交换,就可以凑成回文串(举个栗子:1000 可以在 10001的帮助下变成 0000 和 10101)。凑不出来答案就是n-1。

            多想几组数据还是能想明白的(现场赛你怎么菜的和狗一样?)

    C题

             题面:给出一个n位的数字,如果两个相邻位置的奇偶性不同,那么就可互换他们的位置。互换次数不限,问最终可以获得的最小数字是多少,可以有前导零。其实就是字符序最小。

             做法:遍历一边字符串,然后存下每一个奇数和偶数的位置。然后我们在遍历一边把他们放回去就可以了,放回去的原则是先放小的那个。想想就知道这样的序列是合法的并且是最优的(不会证明,就只能想想)。

     1 #include <stdio.h>
     2 #include <set>
     3 #include <string.h>
     4 using namespace std;
     5 char s[300009], res[300009];
     6 set <int> :: iterator it;
     7 set <int> st[2];
     8 int main()
     9 {
    10     int T, len;
    11     scanf("%d", &T);
    12     while(T--)
    13     {
    14         scanf("%s", s+1);
    15         len = strlen(s+1);
    16         st[0].clear(); st[1].clear();
    17         for(int i = 1; i <= len; i++) 
    18         st[(s[i]-'0')%2].insert(i);
    19         
    20         for(int i = 1; i <= len; i++)
    21         {
    22             if(st[0].empty()) 
    23             {
    24                 res[i] = s[*st[1].begin()]; 
    25                 st[1].erase(st[1].begin());
    26                 continue;
    27             }
    28             if(st[1].empty()) 
    29             {
    30                 res[i] = s[*st[0].begin()]; 
    31                 st[0].erase(st[0].begin());
    32                 continue;
    33             }
    34             
    35              if(s[*st[0].begin()] < s[*st[1].begin()])
    36             {
    37                 res[i] = s[*st[0].begin()]; 
    38                 st[0].erase(st[0].begin());
    39             }else{
    40                 res[i] = s[*st[1].begin()]; 
    41                 st[1].erase(st[1].begin());
    42             }
    43         }
    44         res[len+1] = '\0';
    45         
    46         printf("%s\n", res+1);
    47     }
    48 }

    D题

             题面:说有一个老板要给员工发工资了,每个员工的有一个工资的范围Li和Ri,老板只有S那么多的钱,问怎么发工资才能让所有人工资的中文数最大。

             做法:看到求最大值,然后正向思考发现没有好的贪心策略,那么就可以考虑一手二分。二分的板子一敲,然后问题来了,怎么check。

             比如现在要check  X这个值

             那么对于所有Li  >  X的员工来说只能是在中位数的右边,对于Ri  <  X的员工来说只能是在中位数的左边,对于这两种情况最小花费的增加值都是Li(手算模拟就可以想明白)。其他的就是放哪里都无所谓,可以最优分配。

    先可以数一下必然放左右位置的各有多少,如果其中任意值大于n/2 那么说明是不行的。然后我们把随意放的按Li从小到达排个序,如果左边还没满那么就对所需费用加Li,满了就加X。最后判断费用是否小于老板有的钱就可以。

    但是这里有一个问题,二分要求的是在check的过程中,趋势只有一个,就是大于一个值就都行或者都不行,这样我们才可以二分。但是在这题里面如果X小了或者大了都会出现这样的情况,所以我们需要去掉一个。题目告诉我们,老板最起码可以负担一个最低工资,我们对最低工资排个序,那么二分的下界就是第n/2 + 1个人的 L 。然后就可以很愉快的二分了。

     1 #include <stdio.h>
     2 #include <cstring>
     3 #include <vector>
     4 #include <set>
     5 #include <algorithm>
     6 using namespace std;
     7 struct node
     8 { 
     9     int l, r;
    10     bool operator < (struct node a)
    11     {
    12         return l < a.l;
    13     }
    14 }a[200009];
    15 typedef long long ll;
    16 int T, n, l, r, mid;
    17 ll sum;
    18 bool check(int x)
    19 {
    20     vector <struct node> ve;
    21     ve.clear();
    22     int num = 0;
    23     ll res = 0;
    24     for(int i = 1; i <= n; i++)
    25         if(a[i].r<x) 
    26         {
    27             res += a[i].l;
    28             num++;
    29         }
    30         else if(a[i].l>x) 
    31         {
    32             res += a[i].l;
    33         }else ve.push_back(a[i]);
    34     
    35     if(num>=n/2+1) return false;
    36     
    37     int size = ve.size();
    38     
    39     for(int i = 0; i < size; i++)
    40     if(num<n/2)
    41     {
    42         res += ve[i].l;
    43         num++;
    44     }else res += x;
    45     
    46     return res <= sum;
    47 }
    48 
    49 int main()
    50 {
    51     scanf("%d", &T);
    52     while(T--)
    53     {
    54         scanf("%d %lld", &n, &sum);
    55         for(int i = 1; i <= n; i++)
    56         scanf("%d %d", &a[i].l, &a[i].r);
    57     
    58         r = 1e9;
    59         
    60         sort(a+1, a+1+n);
    61         l = a[n/2+1].l; 
    62     //    printf("%d %d\n", l, r);
    63         while(l<=r)
    64         {
    65             mid = (l+r) / 2;
    66             if(check(mid))
    67             {
    68                 if(!check(mid+1)) break;
    69                 l = mid+1;
    70             }else r = mid - 1;
    71         }
    72         
    73         printf("%d\n", mid);
    74     }
    75 }

    E题

             题面:有n个选民每个人有一个Mi和Pi,Mi表示如果此时已经有那么多人投了你一票,那么他就会跟风投你一票,Pi表示你可以花那么多钱来买下他手里的选票。问最少要多少钱,才能让大家都投你一票。

             做法:对于Mi==0的人来说,他们的选票就是白给的,可以直接拿走,白嫖他。把剩下的人按Mi从小到大排序。然后从后往前遍历,如果现在已经获得的选票+[ i - 1(表示前面的人都把票给你了)] 大于等于Mi那么说明这个人是不需要花钱的,否则的话我们就收买p最小的的选民(包括i在内的已有的选民),可以用multiset或者priority_queue。

     1 #include <stdio.h>
     2 #include <algorithm>
     3 #include <set>
     4 using namespace std;
     5 struct node
     6 {
     7     int m, p;
     8     bool operator < (struct node a)
     9     {
    10         if(m==a.m) return p > a.p;
    11         return m < a.m;
    12     }
    13 }a[200009], tmp;
    14 int main()
    15 {
    16     int T, n, pos, num;
    17     long long res;
    18     scanf("%d", &T);
    19     multiset <int> st;
    20     while(T--)
    21     {
    22         res = pos = num = 0;
    23         scanf("%d", &n);
    24         for(int i = 1; i <= n; i++)
    25         {
    26             scanf("%d %d", &tmp.m, &tmp.p);
    27             if(tmp.m!=0) a[++pos] = tmp;
    28             else num++;
    29         }
    30         sort(a+1, a+1+pos);
    31         st.clear();
    32         for(int i = pos; i >= 1; i--)
    33         {
    34             st.insert(a[i].p);
    35             if(num + i - 1 >= a[i].m) continue;
    36             num++;
    37             res += *st.begin();
    38             st.erase(st.begin());
    39         }
    40     
    41         printf("%lld\n", res);
    42     }
    43 }

            

            

            

  • 相关阅读:
    09.session #
    08.cookie
    07.中间件
    06.类视图
    374. 猜数字大小 力扣 二分 简单却易错
    278. 第一个错误的版本 力扣 二分 简单
    1449. 数位成本和为目标值的最大数字 力扣 动态规划 难 string赋值和比较
    279. 完全平方数 力扣 动态规划 中等
    518. 零钱兑换 II 力扣 动态规划,中等吧
    203. 移除链表元素 力扣
  • 原文地址:https://www.cnblogs.com/loenvom/p/11917820.html
Copyright © 2020-2023  润新知