GCJ Qualification Round 2017
来源:互联网 发布:数据精灵免费版 编辑:程序博客网 时间:2024/05/18 10:20
、#GCJ Qualification Round 2017
第一次参加GCJ资格赛。苟且过了第一关的资格赛。 资格赛一共4题, 给了27个小时的时间。满分100分,高于25分者就有资格参加一周后的 GCJ Round 1。 每题都有small 和 large 两组测试数据。small 可以提交多次, 而 large 只要点击下载了数据,8分钟内必须提交结果和代码,只有一次提交机会。
题目地址:https://code.google.com/codejam/contest/3264486/dashboard
官方题解:https://code.google.com/codejam/contest/3264486/dashboard#s=a
Problem A. Oversized Pancake Flipper
题目大意:
有一排煎饼,正面有笑脸,反面没有。你有一个超大的翻转煎饼的铲子, 一次能且只能翻动K个煎饼, 不能多不能少, 翻动完的煎饼前后顺序与翻动之前不变。给定煎饼的初始状态,’ - ’ 表示 反面, ’ + ’ 表示正面。问至少需要翻动多少下,才能让所有煎饼都正面朝上。如果无法做到,就输出IMPOSSIBLE。
输入格式:
第一行一个数字T,表示测试数据组数。
接下来每行一个字符串,由’ - ’ 和 ’ + ’ 组成,一个数字K。
输出格式:
Case #x: y
x为第几组数据, y为最少要翻动的次数,或者IMPOSSIBLE
样例:
Input
3
—+-++- 3
+++++ 4
-+-+- 4
Output :
Case #1: 3
Case #2: 0
Case #3: IMPOSSIBLE
题解:
对于small 测试数据,用bfs枚举所有可能的翻动情况, 也是可以解决的。但对于 large 测试数据, bfs 8分钟也是跑不完的。。别问我怎么知道的。。
仔细观察,连续K个煎饼的区间,这个区间如果被翻动2次,就等于没有被翻动。所有被翻动的区间的顺序是无所谓的。所以每个不同的区间,最多只可能被翻动1次。我们观察到最左边的煎饼p1只受最左边的翻动f1的影响(f1就是翻动第1~第K个煎饼)。如果第一个煎饼是反面,想改变p1的状态,那就只能执行最左边的翻动f1。接下来同理,f2也只受p2的影响。这样我们就找到了一个贪心的策略。复杂度为O(n^2)
代码:
#include <iostream>using namespace std;int T, K, ans;string str;int main() { std::ios::sync_with_stdio(false); cin >> T; for (int t = 1; t <= T; t++) { cin >> str >> K; ans = 0; int len = str.length(); for (int i = 0; i < len-K+1; i++) { if (str[i] == '-') { ans++; for (int j = i; j < i+K; j++) { if (str[j] == '-') str[j] = '+'; else str[j] = '-'; } } } bool flag = true; for (int i = len-K+1; i < len; i++) if (str[i] == '-') { flag = false; break; } cout << "Case #" << t << ": "; if (flag) cout << ans << endl; else cout << "IMPOSSIBLE" << endl; } return 0;}
Problem B. Tidy Numbers
题目大意:
给定一个正整数N,求最大的小于N的tidy数。tidy数的定义是,从高位到地位的数字为不下降序列。比如122233是tidy数, 20, 321 9990不是tidy数
输入:
第一行 T, 表示测试数据组数
接下来每行一个正整数N
输出:
Case #x: y
其中x表示第x组测试数据,y为满足条件的最大的tidy数
题解:
又一个贪心题。
从高位往低位,找到第一个N[i] > N[i+1]的点,在从i往前找与N[i]相同的数字,找到最左边的N[k] = N[i],然后N[k]到N[i]统统减1,N[i+1]到最后,统统变成9。最后输出的时候再去除前导0,就完成了。\( • ̀ω•́ )/。
代码:
#include <iostream>#include <string>using namespace std;int T;string str;int main() { cin >> T; for (int t = 0; t < T; t++) { cin >> str; int len = str.length(); for (int i = 0; i < len-1; i++) { if (str[i] > str[i+1]) { int k; for (int j = i; j >= 0; j--) { if (str[i] == str[j]) k = j; } str[k]--; for(int j = k+1; j < len; j++) str[j] = '9'; break; } } int k = 0; for (int i = 0; i < len; i++) if (str[i] != '0') { k = i; break; } cout << "Case #" << t+1 << ": "; for (int i = k; i < len; i++) cout << str[i]; cout << endl; } return 0;}
Problem C. Bathroom Stalls
题目大意:
澡堂有N+2个连续的位置,开头和结尾始终有人,所以还有N个空位置。现在有K个人要陆续的来。新来的人总是会找一个距离两边的人距离最远的位置。假定之前来了的人不会中途离开。
具体的讲,设Ls,RS 分别为一个空位置与左边的人的距离 和 这个空位置与右边的人的距离。这个距离指的是间隔的空位置的个数。
1.找一个位置使得min(LS,RS) 最大
2.如果新来的人发现只有一个这样的位置,那就选择这个位置。
3.否则,选择满足条件1的,使得max(LS,RS) 最大的那个。
4. 如果还是有多个选择, 那就选择最左边的。求最后一个人进来时,它选择的位置的
max(LS,RS) 和min(LS,RS)
输入:
第一行 T, 表示测试数据组数
接下来T行,每行两个数字 N, K
输出:
Case #x y z
其中x为测试数据编号, y, z 分别为max(LS,RS),min(LS,RS)
题解:
min(LS,RS)+max(LS,RS)+1=N , 所以min(LS,RS) 和max(LS,RS) 可以同时求出来。
先看特殊情况, 当N=K时,min(LS,RS)=max(LS,RS)=0 ,
当K=1时,max(LS,RS)=N/2,min(LS,RS)=N−N/2−1 。
开始找规律我们定义一个函数
LR(n,k) , 返回一个pair,<max(LS,RS),min(LS,RS)> 。
k = 0 或者 k = n时,min(LS,RS)=max(LS,RS)=0
k = 1时, 就是把N从中间切分成两半, 两半的大小可以相等或者相差1。给他们编号为(1), (2)
k = 2时, 就是把刚刚分成的2半中较大的那一个再切分成两半。假设刚刚(2)比较大,则把(2)分成两半。现在的编号分别为(1), ((3), (4))
k = 3时,这次是把(1)分成2半,现在的编号为((5), (6)), ((3), (4))
…
我们发现,自从第一次把N切分成左右两半后,无论k为何值, 左右两边被切分的次数相差不会超过1。而左右两半的大小相差也不超过1。更大的那一半,切分的次数应该要比较小的那一半要多, 所以我们得到如下递推式:
LR(n,k)=max(LR(n/2,k−k/2),LR(n−n/2−1,k/2))
LR(n/2,k−k/2) 就是较大的一半,LR(n−n/2−1,k/2) 就是较小的一半。由于N的范围太大,无法开数组来进行动态规划,比赛时跑了8分钟也没跑完。第一时间想到的优化策略是,如果切分成的两半的n和k完全相等,则只需要计算一次。然而即使这样还是存在大量的重复计算。
比赛结束后, 想到可以用一个map来存储计算过的状态,记忆化搜索,这样复杂度就降到了O(logn) 。只加了几行代码,large测试数据就直接秒过。
代码:
#include <iostream>#include <cstdio>#include <map>using namespace std;typedef unsigned long long llu;typedef pair<llu, llu> P;map<P, P> Map;int T;llu N, K;P ans;P LR(llu n, llu k) { //记忆化搜索 if (Map.find(make_pair(n, k)) != Map.end()) return Map[make_pair(n, k)]; if (n < k || k <= 0 || n <= 0) return make_pair(0, 0); if (k == 1) { llu mlr = (n+1)/2 - 1; llu Mlr = n - 1 - mlr; return make_pair(Mlr, mlr); } else if (n == k) return make_pair(0, 0); else { P tmp; llu tn = n/2, tk = k/2; if (tn == n-n/2-1 && tk == k-k/2) tmp = LR(tn, tk); else tmp = max(LR(n/2, k-k/2), LR(n-n/2-1, k/2)); return Map[make_pair(n, k)] = tmp;//记录状态 }}int main() { scanf("%d", &T); for (int i = 1; i <= T; i++) { scanf("%llu%llu", &N, &K); ans = LR(N, K); printf("Case #%d: ", i); printf("%llu %llu\n", ans.first, ans.second); } return 0;}
Problem D. Fashion Show
- GCJ Qualification Round 2017
- GCJ Qualification Round 2017 题解(部分)
- GCJ Round 1A 2017 题解
- 【gcj】2014 Round 3
- 【gcj】2013 round 2
- GCJ 2015 Round D
- codejam 2008 Qualification Round
- Code Jam 2017 Qualification Round Problem A. Oversized Pancake Flipper
- Code Jam 2017 Qualification Round Problem B. Tidy Numbers
- Code Jam 2017 Qualification Round Problem C. Bathroom Stalls
- Code Jam 2017 Qualification Round Problem D. Fashion Show
- 【google code jam Qualification Round 2017】【Oversized Pancake Flipper】【贪心】
- 【google code jam Qualification Round 2017】【Tidy Numbers】【搜索】
- 【google code jam Qualification Round 2017】【Bathroom Stalls】
- 2014 GCJ Round 1A
- 2014 GCJ Round 1C
- 【gcj】2012 round 3 待填坑
- 【gcj】2012 round 2 待填坑
- java多线程:7、线程池创建
- 机器视觉之路径规划
- SQL server提示对象名无效,还行执行语句
- 安卓开发:仿微博自定义带进度条和vip标识功能的圆形头像IdentityImageView
- TCP超时与重传
- GCJ Qualification Round 2017
- Python之高级函数
- PS制作GIF 动画
- 交叉编译boa后在开发板运行boa出现-/bin/sh: boa: not found 错误的解决方法
- 题目1436:Repair the Wall 九度OJ
- 递推递归-M-数值分解
- 字典树模板
- Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)
- 设计模式(四)-工厂模式-简单工厂