zoukankan      html  css  js  c++  java
  • 2019牛客暑期多校训练营(第六场)

    传送门

     参考资料:

      [1]:官方题解(提取码:bdv1)

    B.Shorten IPv6 Address(模拟)

    •题意

      已知 ip 地址由 8 位十六进制的数组成;

      例如 ip = 0000:0000:0123:4567:89ab:0000:0000:0000;

      现在让你按照如下规则化简这个 ip 地址:

      (1):前导 0 去掉,例如上述 ip 地址可化简为 ip1 = 0:0:123:4567:89ab:0:0:0;

      (2):连续的 0 字段用 ':' 代替,但只能替换一次,例如,在 ip1 的基础上再进行化简;

          ip2 = ::0123:4567:89ab:0000:0000:0000 或 ip2 = 0000:0000:0123:4567:89ab::;

      (3):再长度最短的情况下使得字典序最小,例如,上述 ip 地址化简中有两个 ip2,取字典序最小的

          ip3 = 0000:0000:0123:4567:89ab:: ;

      给你 128 位二进制表示的字符串,输出简化后的 ip 地址(转化成十六进制);

    •题解

      按照题意模拟即可;

      我是先将二进制数转化成十进制数进行化简操作,然后输出十六进制的 ip 地址;

      需要注意的坑点,假设某化简前的 ip = 0:0:1:0:0:1:0:0;

      将中间的两个 0 字段用 ':' 代替比将末尾的两个 0 字段用 ':' 代替长度更短;

      所以此 ip 地址化简后的结果位 ip = 0:0:1::1:0:0;

    •Code

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define INFll 0x3f3f3f3f3f3f3f3f
      4 #define ll long long
      5 const int maxn=200;
      6 
      7 char s[maxn];
      8 int ip[20];
      9 vector<int >p[20];///p[i]:连续的i个0字段的起始位置
     10 char ch[20]="0123456789abcdef";
     11 
     12 int f(int j)///二进制转十进制
     13 {
     14     int ans=0;
     15     int cnt=0;
     16     for(int i=j+15;i >= j;--i)
     17     {
     18         if(s[i] == '1')
     19             ans += (1<<cnt);
     20 
     21         cnt++;
     22     }
     23     return ans;
     24 }
     25 void OX(int v)///十进制转十六进制
     26 {
     27     if(v/16 == 0)
     28     {
     29         printf("%c",ch[v%16]);
     30         return ;
     31     }
     32     OX(v/16);
     33 
     34     printf("%c",ch[v%16]);
     35 }
     36 void Solve()
     37 {
     38     int k=0;
     39     for(int i=1;k < 8;i += 16)
     40         ip[++k]=f(i);
     41 
     42     for(int i=0;i <= 8;++i)
     43         p[i].clear();
     44 
     45     int index=1;
     46     while(index <= k)///查找连续的0位置
     47     {
     48         for(;index <= k && ip[index] != 0;index++);
     49 
     50         int cnt=0;
     51         int cur=index;
     52 
     53         for(;index <= k && ip[index] == 0;cnt++,index++);
     54         p[cnt].push_back(cur);
     55     }
     56 
     57     for(int i=k;i > 1;--i)///从大到小查找大于两个字段的0位置
     58     {
     59         if(p[i].size() == 0)
     60             continue;
     61 
     62         int cur=p[i][p[i].size()-1];
     63         ///判断cur是否为末尾的连续的0,并且判断中间是否含有相同长度的0字段
     64         if(p[i].size() > 1 && cur+i == 9 && p[i][p[i].size()-2] != 1)
     65             cur=p[i][p[i].size()-2];
     66 
     67         for(int j=1;j < cur;++j)
     68         {
     69             OX(ip[j]);
     70             if(j != cur-1)
     71                 printf(":");
     72         }
     73         printf("::");
     74         for(int j=cur+i;j <= k;++j)
     75         {
     76             OX(ip[j]);
     77             if(j != k)
     78                 printf(":");
     79         }
     80         printf("
    ");
     81         return ;
     82     }
     83 
     84     for(int i=1;i <= k;++i)
     85     {
     86         OX(ip[i]);
     87         if(i != k)
     88             printf(":");
     89     }
     90     printf("
    ");
     91 }
     92 
     93 int main()
     94 {
     95     //freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin);
     96     int T;
     97     scanf("%d",&T);
     98     for(int kase=1;kase <= T;++kase)
     99     {
    100         scanf("%s",s+1);
    101 
    102         printf("Case #%d: ",kase);
    103         Solve();
    104     }
    105     return 0;
    106 }
    View Code

    D.Move(假的二分)

    •题意

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

      有 k 个容积相同箱子装这 n 个物品,按照如下策略装:

      将尽可能大的物品装箱;

    •题解(by官方)

      

      

      对上界为 $lceilfrac{sum}{k} ceil+maxV$ 的理解;

      首先,如果可以将物品拆卸,那么 $lceilfrac{sum}{k} ceil$ 是可以将所有物品都装下的最小整数解;

      那么,因为每个箱子的容积都增加了 maxV;

      那么,对于某个被拆分的物品,便可通过增加的体积使得这个物品装入一个箱子;

      所以, $lceilfrac{sum}{k} ceil+maxV$ 为一个合法的上界;

      这样的话,只需遍历 maxV 次即可找出答案,而 maxV ≤ 1000;

    •Code(multiset+upper_bound)

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

     J.Upgrading Technology(贪心+优先级队列)

    •题意

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

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

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

      但是,如果 $c_{i,j}<0$,则表示从中获得 $left|c_{i,j} ight|$ 的能量;

      定义 $d_{i}$ 表示全部技能都升级到 i 级所获得的能量;

      但是,如果 $d_{i}<0$ 则表示消耗 $left|d_{i} ight|$ 的能量;

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

    •题解

      枚举等级 j;

      判断只使用 d1,d2,...,dj 所获得的能量;

      和至少有一种技能最高等级为 j 时(保证只使用d1,d2,...,dj),其他技能通过 $c_{i,j}<0$ 所获得的最大能量;

      答案取最大值即可;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INFll 0x3f3f3f3f3f3f3f3f
     4 #define ll long long
     5 const int maxn=1e3+50;
     6 
     7 int n,m;
     8 ll c[maxn][maxn];
     9 ll d[maxn];
    10 ll sum[maxn][maxn];///sum[i][j]:第i种技能从0升级到j需要的总消耗
    11 ll Min[maxn][maxn];///Min[i][j]:第i种技能从0升级到j~m需要的总消耗的最小值
    12 ll p[maxn];///p[i]:这n种技能从0升级到i所获得的能量
    13 ll Max[maxn];///Max[i]:这n种技能中的任意少于n种的技能从i级升级到任意等级,从c中获得的最多的能量
    14 priority_queue<ll ,vector<ll >,less<ll > >q;
    15 
    16 ll Solve()
    17 {
    18     for(int i=1;i <= n;++i)
    19     {
    20         sum[i][0]=0;
    21         for(int j=1;j <= m;++j)
    22             sum[i][j]=sum[i][j-1]+c[i][j];
    23     }
    24     for(int i=1;i <= n;++i)
    25     {
    26         Min[i][m+1]=INFll;
    27         for(int j=m;j >= 1;--j)
    28             Min[i][j]=min(Min[i][j+1],sum[i][j]);
    29     }
    30     p[0]=0;
    31     for(int j=1;j <= m;++j)
    32     {
    33         ll cur=0;
    34         for(int i=1;i <= n;++i)
    35             cur += c[i][j];
    36         ///共消耗cur点能量,获得d[j]点能量
    37         p[j]=p[j-1]+d[j]-cur;
    38     }
    39 
    40     Max[0]=Max[m+1]=0;
    41     for(int j=1;j <= m;++j)
    42     {
    43         while(!q.empty())
    44             q.pop();
    45         Max[j]=0;
    46 
    47         for(int i=1;i <= n;++i)
    48         {
    49             ll cur=Min[i][j]-sum[i][j-1];
    50             if(cur < 0)
    51                 q.push(-cur);
    52         }
    53 
    54         int cnt=0;
    55         while(!q.empty())
    56         {
    57             ll cur=q.top();
    58             q.pop();
    59             cnt++;
    60 
    61             if(cnt < n)///取最多n-1个通过c获得的最大能量
    62                 Max[j] += cur;
    63         }
    64     }
    65     ll ans=Max[1];
    66 
    67     for(int i=1;i <= m;++i)
    68         ans=max(ans,p[i]+Max[i+1]);
    69     return ans;
    70 }
    71 int main()
    72 {
    73     int T;
    74     scanf("%d",&T);
    75     for(int _T=1;_T <= T;++_T)
    76     {
    77         scanf("%d%d",&n,&m);
    78         for(int i=1;i <= n;++i)
    79             for(int j=1;j <= m;++j)
    80             scanf("%lld",c[i]+j);
    81         for(int i=1;i <= m;++i)
    82             scanf("%lld",d+i);
    83 
    84         printf("Case #%d: %lld
    ",_T,Solve());
    85     }
    86     return 0;
    87 }
    View Code
  • 相关阅读:
    PAT 1008--------数组元素的循环右移,你需要记住的
    PAT1049-----枚举法,找规律题,注意降低时间复杂度
    PAT1048----你需要了解并记住的解题思路
    C++中几个输入函数的用法和区别(cin、cin.get()、cin.getline()、getline()、gets()、getchar()))
    PAT1040----关于数学题目的解法新思路值得借鉴,字符的配对
    PAT1029-----介绍字符串的解题思路和部分知识点
    PAT1027-----等差数列的问题或数学问题
    PAT1026----四舍五入的思路,%2d的一些知识
    / 已阅 /PAT1017-------高精度计算,问题的所有可能情况
    LeetCode 无重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11296211.html
Copyright © 2011-2022 走看看