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

题目链接:https://ac.nowcoder.com/acm/contest/886#question


A:

光速签到。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 int t;
21 const int maxn = 2e3 + 10;
22 
23 int main() {
24     scanf("%d", &t);
25     for (int ca = 1; ca <= t; ca++) {
26         printf("Case #%d: ", ca);
27         char s[maxn], t[27];
28         scanf("%s", s + 1);
29         scanf("%s", t + 1);
30         map<char, int>m; m.clear();
31         int len = strlen(s + 1), dry = 0, wet = 0, harm = 0;
32         for (int i = 1; i <= len; i++)
33                 if (!m.count(s[i])) m[s[i]] = 1; else m[s[i]]++;
34         for (int i = 1; i <= 26; i++) {
35             if (t[i] == 'd') dry += m[(char)(i + 'a' - 1)];
36             else if (t[i] == 'w') wet += m[(char)(i + 'a' - 1)];
37             else if (t[i] == 'h') harm += m[(char)(i + 'a' - 1)];
38         }
39         if (harm * 4 >= len) puts("Harmful");
40         else if (harm * 10 <= len) puts("Recyclable");
41         else if (!wet || dry / wet >= 2) puts("Dry");
42         else puts("Wet");
43     }
44     return 0;
45 }
View Code

B:

一开始认为可以直接找最长的最靠后的0串进行压缩,后来发现有个大坑:首尾和中间同样长度的0对答案贡献不一样大。在中间的连续0压缩后能少1长度。

可以直接枚举删去哪一段0再计算答案,保证正确。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 10;
21 int a[maxn];
22 
23 int main() {
24     int t; scanf("%d", &t);
25     for (int caseNum = 1; caseNum <= t; caseNum++) {
26         int startPos = 0, maxLen = 0, currLen = 0;
27         for (int i = 1; i <= 8; i++) {
28             a[i] = 0;
29             for (int j = 1; j <= 16; j++) {
30                 int x; scanf("%1d", &x);
31                 a[i] = a[i] * 2 + x;
32             }
33             if (!a[i]) currLen++; // currLen记录当前连续的全0段个数
34             else { // 如果遇到全1段,维护答案
35                 if (currLen >= maxLen && currLen > 1) {
36                     startPos = i - currLen;
37                     maxLen = currLen;
38                 }
39                 currLen = 0;
40             }
41             if (i == 8 && currLen > 1) { // 如果全0段延续到末尾
42                 if (currLen > maxLen) // 如果是最长,照常处理
43                     startPos = i - currLen + 1, maxLen = currLen;
44                 if (currLen == maxLen && startPos == 1) // 否则判断开头有长度跟最大长度相同的全0段
45                     startPos = i - currLen + 1;
46             }
47         }
48         printf("Case #%d: ", caseNum);
49         if (startPos == 1) printf(":");
50         for (int i = 1; i <= 8; i++) {
51             if (i == startPos) {
52                 printf(":"); i += maxLen;
53             }
54             if (i > 8) puts("");
55             else printf("%x%c", a[i], ":\n"[i == 8]);
56         }
57     }
58     return 0;
59 }
View Code

D:

这道题很容易想到二分箱子容积,然后就错了。因为这个题箱子容积跟答案没有单调性!

为了保证正确性,这个题最好的方法就是枚举答案。答案下界显然是max(max(vi),sumV/k),上界则为sumV/k+max(vi),这样容积的箱子一定能放下所有东西。然后再逐个物品check一次就好了。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 1e3 + 10;
21 int v[maxn], puted[maxn];
22 
23 int main() {
24     int t; scanf("%d", &t);
25     for (int caseNum = 1; caseNum <= t; caseNum++) {
26         int n, k, sum = 0; scanf("%d%d", &n, &k);
27         for (int i = 1; i <= n; i++) {
28             scanf("%d", &v[i]); sum += v[i];
29         }
30         sot(v, n);
31         int limV = max(v[n], sum / k);
32         for (int curV = limV;; curV++) {
33             for (int i = 1; i <= n; i++) puted[i] = 0;
34             int cnt = 0;
35             for (int i = 1; i <= k; i++) {
36                 int tmpV = curV;
37                 for (int j = n; j > 0; j--) {
38                     if (!puted[j] && v[j] <= tmpV) {
39                         puted[j] = 1;
40                         tmpV -= v[j];
41                         cnt++;
42                     }
43                 }
44             } if (cnt == n) {
45                 printf("Case #%d: %d\n", caseNum, curV);
46                 break;
47             }
48         }
49     }
50 }
View Code

G:

一看就是蔡勒公式相关题。通过一些简单性质可以进行剪枝:日份第一位只能是0123,月份第一位只能是0和1,年份第一位不可能为0。而且合法日期+星期五的限制很紧,题解里谈到对于任何一个加密后的日期,只有几万个排列能将其映射到合法日期。故可只对前弱冠个日期求出所有合法的排列之后,再依次检查是否对所有日期合法。注意要对重复日期去重。

顺便这里补充一下蔡勒公式

对于1752年9月3日前的日期,ans = (d+2*m+3*(m+1)/5+y+y/4+5) % 7;而对于1752年9月3日后的日期,ans = (d + 2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7。这是因为罗马教皇决定在1582年10月4日后使用格利戈里历法,而英国则是在1752年9月3日后才接受使用格利戈里历法。要注意这个点。

计算出来的答案从0到6分别为星期日到星期六。使用时要注意,对于1月和2月的日期,要变为上一年的13月和14月参与计算。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 int a[15], mon[15] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
21 const int maxn = 1e5 + 10;
22 string s[maxn];
23 
24 int check(int x) {
25     int year = 0, month = a[s[x][5] - 'A'] * 10 + a[s[x][6] - 'A'], day = a[s[x][8] - 'A'] * 10 + a[s[x][9] - 'A'];
26     for (int i = 0; i < 4; i++)
27         year = year * 10 + a[s[x][i] - 'A'];
28     if (year % 4 == 0 && year % 100 || year % 400 == 0) mon[2] = 29;
29     else mon[2] = 28;
30     if (year < 1600 || year > 9999 || month < 1 || month > 12 || day < 1 || day > mon[month])
31         return 0;
32     if (month <= 2) month += 12, year--;
33     int ans = (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400 + 1) % 7;
34     if (ans == 5) return true;
35     else return false;
36 }
37 
38 int main() {
39     int t; scanf("%d", &t);
40     for (int caseNum = 1; caseNum <= t; caseNum++) {
41         printf("Case #%d: ", caseNum);
42         for (int i = 0; i < 10; i++) a[i] = i;
43         int n; scanf("%d", &n);
44         for (int i = 1; i <= n; i++) cin >> s[i];
45         sot(s, n);
46         n = unique(s + 1, s + 1 + n) - s - 1;
47         int tmp = min(n, 20), flag = 1;
48         do {
49             int haveSolution = 1;
50             for (int i = 1; haveSolution && i <= tmp; i++)
51                 if (!check(i)) haveSolution = 0;
52             if (haveSolution)
53                 for (int i = tmp + 1; haveSolution && i <= n; i++)
54                     if (!check(i)) haveSolution = 0;
55             if (haveSolution) {
56                 for (int i = 0; i < 10; i++) printf("%d", a[i]);
57                 puts("");
58                 flag = 0;
59                 break;
60             }
61         } while (next_permutation(a, a + 10));
62         if (flag) puts("Impossible");
63     }
64     return 0;
65 }
View Code

J:

一个人有n个技能,每个技能最高可以升到m级。初始状态下n个技能的等级都是0级。第i个技能从(j-1)级升到j级要花费cij点代价(有可能是负数)。如果所有技能都达到了j级,就可以获得dj点代价(有可能是负数)。输出这个人能获得的最大代价是多少。

这个题很容易想到贪心,然后就踩坑了:枚举有j个等级升满,然后对第i种技能从pre[i][j]到pre[i][m]中选择最小的那个作为第i种等级的最终等级。pre[i][j]表示第i种技能升j级所需代价和。这种做法的问题在于d[j]可能是负的,贪心选最小的pre可能导致j+1之后某些负的level使得答案变小。可以这样修正得到正解:枚举一种等级i作为level最低的技能,并枚举它的等级j,那么其他技能的等级可以在j到m之间任取。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 1e3 + 10;
21 ll c[maxn][maxn], d[maxn], sum[maxn], val[maxn], tem[maxn];
22 int n, m;
23 
24 int pos(ll *c, int l, int r) {
25     ll val = ll_inf; int minI;
26     for (int i = l; i <= r; i++)
27         if (c[i] <= val) {
28             val = c[i];
29             minI = i;
30         }
31     return minI;
32 }
33 
34 int main() {
35     int t; scanf("%d", &t);
36     for (int casenum = 1; casenum <= t; casenum++) {
37         scanf("%d%d", &n, &m);
38         for (int i = 1; i <= n; i++) sum[i] = 0;
39         for (int i = 1; i <= n; i++) {
40             c[i][0] = 0; val[i] = ll_inf;
41             for (int j = 1; j <= m; j++) {
42                 scanf("%lld", &c[i][j]);
43                 sum[j] += c[i][j];
44                 c[i][j] += c[i][j - 1];
45                 if (c[i][j] <= val[i]) {
46                     val[i] = c[i][j];
47                     tem[i] = j;
48                 }
49             }
50         }
51         d[0] = 0;
52         for (int i = 1; i <= m; i++) {
53             scanf("%lld", &d[i]);
54             d[i] -= sum[i]; d[i] += d[i - 1];
55         }
56         ll ans = 0, tmp, maxv;
57         for (int i = 0; i <= m; i++) {
58             tmp = d[i]; int cnt = 0;
59             if (i != m) {
60                 ll minv; maxv = -ll_inf;
61                 for (int j = 1; j <= n; j++) {
62                     if (tem[j] <= i) tem[j] = pos(c[j], i + 1, m);
63                     minv = c[j][tem[j]];
64                     if (minv - c[j][i] < 0) {
65                         maxv = max(maxv, minv - c[j][i]);
66                         tmp -= minv - c[j][i];
67                         cnt++;
68                     }
69                 }
70             }
71             if (cnt == n) tmp += maxv;
72             ans = max(ans, tmp);
73         }
74         printf("Case #%d: %lld\n", casenum, ans);
75     }
76     return 0;
77 }
View Code

也可以这样做:显然升级过程没有后效性,考虑dp。设dp[i][j]为前i种科技最小等级为j时,所付出的最小代价。

状态转移方程dp[i+1][j]=min(dp[i][k]+pre[i+1][u]),其中k>=j,u>=j,且k和u至少有一个为j。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 2e3 + 10;
21 ll a[maxn][maxn], sum[maxn], dp[maxn][maxn][20];
22 int n, m;
23 
24 void init() {
25     for (int i = 1; i <= n; i++)
26         for (int j = 1; j <= m + 1; j++)
27             dp[i][j][0] = a[i][j];
28     for (int k = 1; k <= n; k++)
29         for (int j = 1; (1 << j) <= m + 1; j++)
30             for (int i = 1; i + (1 << j) - 1 <= m + 1; i++)
31                 dp[i][k][j] = min(dp[i][k][j - 1], dp[i][k + (1 << j - 1)][j - 1]);
32 }
33 
34 ll rmq(int j, int l, int r) {
35     int k = log2(r - l + 1);
36     return min(dp[j][l][k], dp[j][r - (1 << k) + 1][k]);
37 }
38 
39 int main() {
40     int t; scanf("%d", &t);
41     for (int caseNum = 1; caseNum <= t; caseNum++) {
42         scanf("%d%d", &n, &m);
43         for (int i = 1; i <= n; i++)
44             for (int j = 2; j <= m + 1; j++) {
45                 scanf("%lld", &a[i][j]); // 同一技能升级代价做前缀和
46                 a[i][j] += a[i][j - 1];
47             }
48         init();
49         for (int i = 2; i <= m + 1; i++) {
50             scanf("%lld", &sum[i]); // 技能最低等级奖励也做前缀和
51             sum[i] += sum[i - 1];
52         }
53         ll ans = 0;
54         for (int j = 1; j <= m + 1; j++) {
55             ll tmp1 = 0;
56             for (int i = 1; i <= n; i++) tmp1 += rmq(i, j, m + 1);
57             for (int i = 1; i <= n; i++) {
58                 ll tmp = tmp1;
59                 tmp -= rmq(i, j, m + 1); tmp += a[i][j];
60                 tmp = sum[j] - tmp; ans = max(ans, tmp);
61             }
62         }
63         printf("Case #%d: %lld\n", caseNum, ans);
64     }
65     return 0;
66 }
View Code

 

posted @ 2019-08-03 23:58  JHSeng  阅读(241)  评论(0编辑  收藏  举报