2019牛客暑期多校训练营(第六场)

传送门

A.Garbage Classification(阅读理解)

•题意

给你一个由小写字母组成的字符串

然后给26个字母所对应的h,d,w,代表垃圾的有害性,干湿性

如果所有有害性的字母的总和>=总字母数的25%则是有害垃圾

如果所有有害性的字母的总和<=总字母数的10%则是可回收垃圾

如果干性字母的总和>=2倍的湿性垃圾字母则是干垃圾

否则是湿垃圾

•思路

吐槽题面,也太难理解了叭...

记录一下每个字母的个数

再根据26个字母的性质判断是h,d还是w

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string s,t;
 4 int num[30];
 5 int main()
 6 {
 7     int tt;
 8     cin>>tt;
 9     for(int k=1;k<=tt;k++)
10     {
11         memset(num,0,sizeof num);
12         int d=0,w=0,h=0;
13         cin>>s>>t;
14         int len=s.length();
15         for(int i=0;i<len;i++)
16             num[s[i]-'a']++;
17         for(int i=0;i<26;i++)
18         {
19             if(t[i]=='h')
20                 h+=num[i];
21             else if(t[i]=='d')
22                 d+=num[i];
23             else if(t[i]=='w')
24                 w+=num[i];
25         }
26 
27         int aa=(len/4+(len%4 == 0 ? 0:1));
28         int bb=len/10;
29         printf("Case #%d: ",k);
30         if(h>=aa)
31             puts("Harmful");
32         else if(h<=bb)
33             puts("Recyclable");
34         else if(d>=2*w)
35             puts("Dry");
36         else
37             puts("Wet");
38     }
39 }
View Code

B.Shorten IPv6 Address(进制转换+模拟)

•题意

给你128位二进制数,为一个IPV6的地址

按照IPV6的规则,化成长度最短的字典序最小的IPV6地址

IPV6规则:

①用十六进制表示地址,并使用冒号':'分隔每四个十六进制数字。每四个数字称为一个字段。

  例如 0000:0000:0123:4567:89ab: 0000:0000:0000”

②字段中的前导零可以省略

  上述可化简为0:0:123:4567:89ab:0:0:0:0

③由至少两个字段组成的连续零字段(附近有冒号)可以用双冒号'::'替换。地址中最多只能使用一个双冒号。

  上述IPv6地址可以缩写为 0:0:123:4567:89ab:: 或 ::123:4567:89ab:0:0:0,但不能 ::123:4567:89ab::

注意!!!

一个0字段不能省略!!!

我在这里卡了好久好久,最后还是鹏哥哥帮我找到的bug

此致,敬礼∠(°ゝ°)

•思路

利用二进制转化为十六进制

二进制数四位一组可以转化成一位十六进制数

转化为十六进制数后 处理一下前导0

再找连续0字段的最大长度,然后再选择字典序较小的那个

找最大长度的注意事项:

如果首中尾0字段相同,选择中的0字段,因为会多减少一个冒号“:”

例如 0:0:1:0:0:1:0:0

①  : :1:0:0:1:0:0    长度为13

②  0:0:1::1:0:0      长度为12

③  0:0:1:0:0:1::     长度为13

其中②的长度最小

找较小字典序的注意事项:

删除比较靠后的那个,但是要同时注意上面的最大长度问题

例如:  1:0:0:1:0:0:1:1

①   1::1:0:0:1:1

②   1:0:0:1::1:1

其中②的字典序比较小

例如: 0:0:0:1:1:0:0:0

①::1:1:0:0:0

②0:0:0:1:1::
②的字典序比较小

总结起来说就是先找0字段长度最大的

当0字段长度相等时

先找中间那个,因为长度最小

没有中间那个就找后边那个,因为字典序较小

•代码

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 char s[200];
  4 char a[40];
  5 int flag=0;
  6 
  7 void trans(char ss[])//二进制转十六进制
  8 {
  9     for(int i=1;i<=8;i++)
 10     {
 11         for(int j=1;j<=4;j++)
 12         {
 13             int num=0;
 14             for(int k=0;k<4;k++)
 15             {
 16                 int cur=(i-1)*16+(j-1)*4+k;
 17                 int x=ss[cur]-'0';
 18                 num=(num<<1)+x;
 19             }
 20             char after='!';
 21             if(num<10)
 22                 after=char('0'+num);
 23             else
 24                 after=char('a'+(num-10));
 25 
 26             flag++;
 27             a[flag]=after;
 28         }
 29     }
 30 }
 31 
 32 
 33 int main()
 34 {
 35 //    freopen("C:/Users/14685/Desktop/stdin&&stdout/contest","r",stdin);
 36     int tt;
 37     cin>>tt;
 38     for(int k=1;k<=tt;k++)
 39     {
 40         flag=0;
 41         scanf("%s",s+1);
 42         trans(s+1);
 43 
 44         for(int i=1;i<=8;i++)//找前导0,并置为 *
 45         {
 46             int one=0;
 47             flag=1;
 48             while(one==0&&flag<=4)
 49             {
 50                 int now=(i-1)*4+flag;
 51                 if(a[now]=='0'&&flag!=4)
 52                 {
 53                     a[now]='*';
 54                     flag++;
 55                     continue;
 56                 }
 57                 one++;
 58                 break;
 59             }
 60         }
 61 
 62         bool vis[10]={0};      //vis[1~8] 代表8个字段,其中0字段为0,非0字段为1
 63         for(int i=1;i<=32;i+=4)
 64         {
 65             if(a[i]=='*'&&a[i+1]=='*'&&a[i+2]=='*'&&a[i+3]=='0')
 66                     vis[(i+3)/4]=true;
 67         }
 68 
 69         int index=1;
 70         int ans=0;
 71         int ansi=0;
 72 
 73         while(index <= 8)//找最大0字段
 74         {
 75             for(;index <= 8 && vis[index] == 0;index++);
 76 
 77             int cnt=0;
 78             int cur=index;
 79             for(;index <= 8 && vis[index];index++,cnt++);
 80 
 81             if(cnt < 2)  //0字段为1个的情况,坑点!
 82                 continue;
 83             if(cnt >= ans) //总结的代码表达
 84             {
 85                 if(cnt==ans&&cur+cnt-1==8&&ansi!=1)//长度相等先找中间
 86                     continue;
 87                 ans=cnt;//最大0字段长度
 88                 ansi=cur;//最大0字段开始位置
 89             }///ansi~ansi+ans-1
 90         }
 91 
 92         printf("Case #%d: ",k);
 93         for(int i=1;i<=32;i++)
 94         {
 95             if(ans >= 2 && i==ansi*4)  //时刻注意0字段长度大于1才能省略
 96                 printf("::");
 97             if(ans >= 2 &&i>=ansi*4&&i<=(ansi+ans-1)*4)
 98                 continue;
 99             if(a[i]!='*')
100                 printf("%c",a[i]);
101             if(i%4==0&&i!=(ansi-1)*4&&i!=32)
102                 printf(":");
103         }
104         printf("
");
105     }
106 }
View Code

D.Move (multiset)

•题意

  有 n 个物品,第 i 个物品体积为 vi

  有 k 个容积相同箱子装这 n 个物品

  装箱策略是 装尽可能大的

  即 装小于等于剩余体积的最大物品

  

•思路

我们只能一个一个枚举去试

箱子最小体积是sum/k,也就是不浪费 一点空间全部装满

那我们就从这个下界开始,体积逐步增大,

肯定会存在某个体积把物品全都装下

把物体体积放入 multiset中,便于查找小于等于空闲体积的最大物品和删除物品

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1005;
 4 int a[maxn];
 5 int n,k;
 6 
 7 bool check(int V)
 8 {
 9     multiset<int> s;
10 
11     for(int i=1;i<=n;i++)
12         s.insert(a[i]);
13 
14     for(int i=1;i<=k;i++)
15     {
16         int cur=V;
17         while(cur&&!s.empty())
18         {
19             auto it=s.upper_bound(cur);
20             if(it==s.begin())
21                 break;
22             it--;
23             cur-= *it;
24             s.erase(it);
25         }
26     }
27     if(s.empty())
28         return true;
29     else
30         return false;
31 }
32 
33 int main()
34 {
35     int t;
36     cin>>t;
37     for(int _t=1;_t<=t;_t++)
38     {
39         cin>>n>>k;
40         int sum=0;
41         for(int i=1;i<=n;i++)
42         {
43             cin>>a[i];
44             sum+=a[i];
45         }
46         for(int i=(sum+k-1)/k;;i++)
47         {
48             if(check(i))
49             {
50                 printf("Case #%d: %d
",_t,i);
51                 break;
52             }
53         }
54     }
55 }
View Code

 

J.Upgrading Technology(贪心+前缀和)

•题意

  有 n 种技能,每种技能的最大等级为 m;

  初始,这 n 种技能的等级全部为 0;

  定义 Ci,j 表示第 i 种技能从等级 j-1 升级为等级 j 需要消耗 Ci,j 的能量;

  但是,如果 Ci,j<0,则表示从中获得 |Ci,j|的能量;

  定义 di 表示全部技能都升级到 i 级所获得的能量;

  但是,如果 di<0则表示消耗 |di| 的能量;

  每种技能可以升级到 0~m 任意等级,求最终获得的最大能量是多少;

•思路

定义ai,j为第i种技能前j等级所需要能量的前缀和,

试想如果第j等级需要消耗p能量,而第(j+1)等级会获得q能量

因为我们想尽可能的多获得能量,那我们就把会获得能量的升级和它前面的升级捆绑在一起

即上面从第(j-1)级升到第(j+1)级消耗(q-p)能量,从第(j-1)级升到第j级也是消耗(q-p)能量,因为后面的q能量,不加白不加∠(°ゝ°)

那用代码如何实现呢

可以根据前缀和ai,j逆序找最大值!Maxi,j代表第i种能量至少到第j等级消耗的能量,实际等级是(j~m)之间,毕竟白送能量,不加血亏

Maxi,j-ai,j即为白加的能量

白加能量也有一定的限制鸭,每全部到达一个等级j,会得到或消耗一个dj的能量

那我们就从1到m枚举这个等级

在加能量时要注意有白加的能量,也就是实际等级可能比j高

然后每次处理一下,防止等级越过

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e3+5;
 5 ll a[maxn][maxn],mx[maxn][maxn];
 6 ll d[maxn];
 7 int main()
 8 {
 9     int t;
10     scanf("%d",&t);
11     for(int Case=1;Case<=t;Case++)
12     {
13         int n,m;
14         scanf("%d%d",&n,&m);
15 
16         ll aij;
17         for(int i=1;i<=n;i++)
18         {
19             for(int j=1;j<=m;j++)
20             {
21                 scanf("%lld",&aij);
22                 a[i][j]=a[i][j-1]-aij;//第i种技能前j等级消耗能量前缀和
23             }
24             mx[i][m]=a[i][m];
25             for(int j=m-1;j>=0;j--)   //逆序找最大的
26                 mx[i][j]=max(mx[i][j+1],a[i][j]);
27         }
28 
29         for(int j=1;j<=m;j++)
30             scanf("%lld",&d[j];)
31 
32         ll dsum=0,ans=0;
33         for(int j=0;j<=m;j++)//枚举每个等级
34         {
35             dsum+=d[j];
36             ll ansj=dsum;
37             for(int i=1;i<=n;i++)//先全部加上能量 注意有白加的能量
38                 ansj+=mx[i][j];
39             for(int i=1;i<=n;i++)//因为有白加的等级,所以要减去白加等级能获得的最小的能量mx[i][j]-a[i][j]
40                 ans=max(ans,ansj-mx[i][j]+a[i][j]);//没有白加的话就是减去0了
41         }
42         printf("Case #%d: %lld
",Case,ans);
43     }
44 }
View Code
原文地址:https://www.cnblogs.com/MMMinoz/p/11296299.html