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("\n");
 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("\n");
 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官方)

  

  

  对上界为 $\lceil\frac{sum}{k}\rceil+maxV$ 的理解;

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

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

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

  所以, $\lceil\frac{sum}{k}\rceil+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\n",_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}\right|$ 的能量;

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

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

  每种技能可以升级到 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\n",_T,Solve());
85     }
86     return 0;
87 }
View Code

 

posted @ 2019-08-03 20:40  HHHyacinth  阅读(281)  评论(0编辑  收藏  举报