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 }

 

 

 

 

        

 

 

 

        

 

        

posted @ 2019-11-23 14:16  loenvom  阅读(215)  评论(0编辑  收藏  举报