ACM-ICPC 2015 Mid-Central Regional

题目链接

http://acm.uestc.edu.cn/#/problem/show/1358

MCPC2015的题目,CDOJ上是2016 UESTC ACM Summer Training

直接看题

 


 Problem A ACM Contest Scoring

简单的模拟题,给出了OJ的提交记录,让你计算出AC数目和罚时

总的实现和2017河工大邀请赛A题差不多。

和那个相比,这个省去了记录每个人的情况并进行排序的过程;

另外也可以改进一下直接在线做。

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main(){
 5     int t, penalty[256], ans1 = 0, ans2 = 0;
 6     char id;
 7     string s;
 8     bool ac[256];
 9     memset(ac, 0, sizeof(ac));
10     memset(penalty, 0, sizeof(penalty));
11     while(cin >> t >> id >> s && t != -1){
12         if(!ac[id]){
13             if(s == "right"){
14                 ac[id] = true;
15                 ans1 ++;
16                 ans2 += penalty[id] + t;
17             }else
18                 penalty[id] += 20;
19         }
20     }
21     cout << ans1 << ' ' << ans2;
22     return 0;
23 }

 


 Problem C Dance Recital

给出若干字符串(仅由A-Z组成),询问前后两个字符串的相似字母个数。

对于这些字符串的所有排列,求一个最小排列使得上述询问的结果最小。

题目数据很小,只有10个字符串,直接暴力枚举可行(没错,next_permutation可以过)。

也可以用回溯法手写求排列的过程

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, ans = 0x3f3f3f3f;
 4 char s[12][30];
 5 int change[12][12], ns[12], id[12];
 6 
 7 int main(){
 8     scanf("%d", &n);
 9     for(int i = 0; i < n; i++){
10         scanf("%s", s[i]);
11         ns[i] = strlen(s[i]);
12         id[i] = i;
13     }
14     for(int i = 0; i < n; i++){
15         for(int j = i+1; j < n; j++){
16             int tot = 0;
17             for(int k1 = 0, k2 = 0; k1 < ns[i] && k2 < ns[j]; ){
18                 if(s[i][k1] == s[j][k2])
19                     k1++, k2++, tot++;
20                 else if(s[i][k1] < s[j][k2])
21                     k1++;
22                 else k2++;
23             }
24             change[i][j] = change[j][i] = tot;
25         }
26     }
27     while(next_permutation(id, id+n)){
28         int cur = 0;
29         for(int i = 0; i < n-1; i++) cur += change[id[i]][id[i+1]];
30         ans = min(ans, cur);
31     }
32     printf("%d", ans);
33     return 0;
34 }
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, ans;
 4 char s[12][30];
 5 int change[12][12], ns[12];
 6 bool vis[12];
 7 int id[12];
 8 
 9 void dfs(int k){
10     if(k == n){
11         int cur = 0;
12         for(int i = 0; i < n-1; i++) cur += change[id[i]][id[i+1]];
13         ans = min(ans, cur);
14     }else
15         for(int i = n-1; i >= 0; i--)
16             if(!vis[i]){
17                 id[k] = i;
18                 vis[i] = true;
19                 int cur = 0;
20                 if(k > 1){
21                     for(int i = 0; i < k-1; i++) cur += change[id[i]][id[i+1]];
22                     if(cur < ans) dfs(k+1);
23                 }else dfs(k+1);
24                 vis[i] = 0;
25             }
26 }
27 
28 int main(){
29     scanf("%d", &n);
30     for(int i = 0; i < n; i++) scanf("%s", s[i]);
31     for(int i = 0; i < n; i++) ns[i] = strlen(s[i]);
32     for(int i = 0; i < n; i++){
33         for(int j = i+1; j < n; j++){
34             int tot = 0;
35             for(int k1 = 0, k2 = 0; k1 < ns[i] && k2 < ns[j]; ){
36                 if(s[i][k1] == s[j][k2])
37                     k1++, k2++, tot++;
38                 else if(s[i][k1] < s[j][k2])
39                     k1++;
40                 else k2++;
41             }
42             change[i][j] = change[j][i] = tot;
43         }
44     }
45     memset(vis, 0, sizeof(vis));
46     memset(id, -1, sizeof(id));
47     ans = 0x3f3f3f3f;
48     dfs(0);
49     printf("%d", ans);
50     return 0;
51 }
回溯法

 


 Problem D Hidden Password

给出两个字符串s1和s2,检查s1中的每一个字符是不是按约定顺序依次出现在s2中。

首先看s1的所有字符中,在s2中第一个出现的。

如果它不是s1最前面的字符的话,FAIL。

接着去掉s1第一个字符,对新的字符串继续进行上述操作。

实现的很乱,标准题解有相当好的STL解法。

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main(){
 5     char pwd[10], str[50];
 6     char set[255], vis[255], flag = true;
 7     int i, j;
 8     memset(set, 0, sizeof(set));
 9     memset(vis, 0, sizeof(vis));
10     scanf("%s%s", pwd, str);
11     int np = strlen(pwd), ns = strlen(str);
12     for(i = 0; i < np; i++) set[pwd[i]]++;
13 
14     for(i = 0, j = 0 ; i < ns; i++){
15 // printf("\n%d %d %c %d %c %d ", i, j, str[i], set[str[i]], pwd[j], set[pwd[j]]);
16         if(set[str[i]] > 0){
17             if(str[i] != pwd[j]){
18                 // printf(" fail ");
19                 flag = false;
20                 break;
21             }
22             set[str[i]]--;
23             j++;
24             // if(set[pwd[j]] == false) set[pwd[j]] = true;
25         }
26     }
27     if(j < np) flag = false;
28     printf("%s", flag ? "PASS" : "FAIL");
29     return 0;
30 }

 


 Problem F Line Them Up

检查若干个名字是不是严格升序排列或者严格降序排列

有很多种想法,例如看前后两个的strcmp值是不是一致的(一致即有序,此时需要判断一下是1还是-1来确定是不是升序)。

或者排序后看看是不是和原来的是一致的,或者是相反的(时间复杂度稍高)。

或者各种脑洞写法。

代码 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main(){
 5     vector<string> ord1, ord2;
 6     int n;
 7     string s;
 8     cin >> n;
 9     for(int i = 0; i < n; i++){
10         cin >> s;
11         ord1.push_back(s);
12         ord2.push_back(s);
13     }
14     sort(ord1.begin(), ord1.end());
15     bool isInc = true, isDec = true;
16     for(int i = 0, j = n-1; i < n; i++, j--){
17         if(ord1[i].compare(ord2[i]) != 0) isInc = false;
18         if(ord1[i] != ord2[j]) isDec = false;
19     }
20     if(isInc && !isDec) s = "INCREASING";
21     if(!isInc && isDec) s = "DECREASING";
22     if(!isInc && !isDec) s = "NEITHER";
23     cout << s;
24     return 0;
25 }

 


 Problem H Pyro Tubes

给出若干个整数(升序),对于第i个整数,看它后面有多少个和它在二进制上相差在2以内的整数,计算出它们个数。

最省事的想法就是暴力法O(n2),计算两个数的Xor值,看这个值里面有几个1,但显然TLE。

暴力的卡点在于计算Xor结果中1的个数。位运算效率很高,但是判断、计数的引入严重影响常数。

那就枚举相差的位置,题目中说明这些数都是18位二进制数以内,因此枚举量实际上是n*(1+..+18+18)。

注意使用Xor的特性,即反转特定二进制位。

注意本题要求统计每个数后面的情况,因此需要判断后一个数是否大于这个数。

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int M = 18;
 4 set<int> s;
 5 
 6 int main(){
 7     int t;
 8     while(scanf("%d", &t) == 1 && t != -1) s.insert(t);
 9 
10     for(set<int>::iterator it = s.begin(); it != s.end(); it++){
11         int ans = 0, cur = *it;
12         for(int i = 0; i < M; i++){
13             // reverse i-th bit
14             int t = cur ^ (1 << i);
15             if(t > cur && s.count(t)) ans++;
16             for(int j = 0; j < i; j++){
17                 int k = t ^ (1 << j);
18                 if(k > cur && s.count(k)) ans++;
19             }
20         }
21         printf("%d:%d\n", cur, ans);
22     }
23     return 0;
24 }

  


 Problem I Word Clouds Revisited

给出若干个小长方形放进一个大长方形容器中,约定如下放的方法:

1. 长方形之间按行放置,每行的长方形总宽度不能超过容器的宽度,行高按这行长方形中高度最大的计算。

2. 每个长方形必须放在前一个后面(同一行)或者另起新行。

基于此使用动态规划,记F[i]表示前i个长方形放进容器后的累计最大行高,则有(在能放下的情况下),

F[i] = max(放在上一行结尾,拉上一行若干个长方形到下一行组成新行,自己组成新行)

其中,放在上一行结尾等价于拉上一行所有长方形到下一行(主要为了代码简便)。

于是状态转移方程这样写

F[i] = max( F[i-1] + h[i] , F[j-1] + max(h[j]) ) , j 属于上一行

由于要求长方形按顺序放置,故j要逆序枚举,枚举范围是j加到i的宽度小于等于容器宽度。

代码 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 5000+10;
 4 int n, c;
 5 int w[maxn], h[maxn];
 6 int dp[maxn];
 7 // dp[i] = max(dp[i-1]+h[i](when >), dp[j]+max(h[j+1],,h[i])(when <))
 8 
 9 int main(){
10     scanf("%d%d", &n, &c);
11     for(int i = 1; i <= n; i++)
12         scanf("%d%d", w+i, h+i);
13     memset(dp, 0, sizeof(dp));
14     dp[0] = 0;
15     dp[1] = h[1];
16     for(int i = 2; i <= n; i++){
17         dp[i] = dp[i-1] + h[i]; // place on next line
18         // other situations
19         int curw = w[i], curh = h[i];
20         for(int j = i-1; j > 0; j--){
21             // printf("in %d %d", i, j);
22             curw += w[j];
23             if(curw > c) break;
24             curh = max(curh, h[j]);
25             // printf(" was %d %d ", dp[i], dp[j-1] + curh);
26             dp[i] = min(dp[i], dp[j-1] + curh);
27             // printf("in%d %d\n", i, dp[i]);
28         }
29     }
30     printf("%d", dp[n]);
31     return 0;
32 }

 


 

转载于:https://www.cnblogs.com/misaka14522/p/7043179.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值