2017 google Round D APAC Test 题解
首先说明一下:我只是用暴力过了4道题的小数据,就是简单的枚举,大数据都不会做!下面的题解,是我从网上搜到的解答以及查看排行榜上大神的答案得出来的。
首先贴一下主要的题解来源:http://codeforces.com/blog/entry/47796,基本上解题思路都是从这里看到的,你可以直接查看这个链接,或者看下面的题解。
这个解题报告也很好:http://blog.csdn.net/zhoufenqin/article/details/52840475
还有排行榜大神们的答案,https://code.google.com/codejam/contest/5264486/scoreboard?c=5264486#vf=1,你可以下载所有的答案,进行查看。
当然,首先你得看懂题意,题目从官网上查看,这里就不介绍了。
第一题
1.看完题,怎么感觉这道这么难,难道不是第一道题最简单的套路了么?其实,如果你知道那个问题的话,答案非常简单。
简单的想法,就是枚举合法的序列,长度为(n + m),枚举过程中保证合法性,最后把所有的结果加起来,就是最后的答案,注意,这里求的是概率,所有可能的个数是(n + m)!,而且最后串的长度也是n+m,可以考虑每一次枚举除以因子,从1到n+m,这样算出来就是概率。这里小数据很容易就过了,小数据的数据范围是20,2^20 = 1e6,可以满足要求。大数据是4000,2^4000,根本不行,所以考虑其他途径。
2. 那就是动态规划,枚举当前a和b得票的个数,记dp[a][b],考虑最后一次是投给谁的票,然后可以从dp[a - 1][b]和dp[a][b-1]进行转移,注意这个过程中转移的条件,以及a>b必须满足,其他的dp[a][b] = 0;最后的结果是dp[n][m].这个计算过程也要考虑上面的分母因子。
void solve() {
int x, y;
cin >> x >> y;
memset(dp, , sizeof dp);
dp[][] = ;
for (int i = ; i <= x; i++) {
for (int j = ; j <= y; j++) {
if(i <= j) continue;
dp[i][j] = dp[i - ][j] * (x - i + ) / (x + y - i - j + );
if(i >= j + && j > )
dp[i][j] += dp[i][j - ] * (y - j + ) / (x + y - i - j + );
}
}
printf("%.10f\n", dp[x][y]);
}
第一名比较巧妙的递推:
void init() {
for (int i = ; i <= ; i++) dp[i][] = ;
for (int i = ; i <= ; i++) {
for (int j = ; j < ; j++) {
if(i <= j) continue;
double p1 = 1.0 * i / (i + j);
double p2 = - p1;
dp[i][j] = dp[i - ][j] * p1 + dp[i][j - ] * p2;
}
}
}
3. 其实答案就是(n - m)/(n + m), https://en.wikipedia.org/wiki/Bertrand%27s_ballot_theorem,可以看一下证明过程
第二题
1. 小数据5*5,一共有2^25 = 32000000种方式,然后检查一下是否满足条件,最后找到满足要求的最大值。注意这里的情况可能有25种输入,而小数据的测试数据是100,也就是每种情况可以重复4次,可以提前算出所有答案,进行查表。
2. 大数据是100,100,无法进行枚举,然后就观察情况,找规律。可以发现下面的规律。
3. (r,c)和(c,r)的答案是一样的,所以只考虑r<=c的情况。r = 1的时候,答案是 c - c / 3, 例如110110110110这样。r = 2的时候,ans= 2*(c - c / 3),答案是r=1的时候重复2行。下面考虑其他情况。
4. r >= 3.观察
110110110110
101101101101
011011011011
可以发现每三行这样重复,一定是满足要求的,而且这样摆放是最多的,因为每三个至少有一个空白,上面的摆法就是没3个一个空白。然后根据r%3的情况考虑那一行应该放前面。
r%3 == 0, 随意都可以,观察,答案就是 r / 3 * c * 2, 3行一组, 每一组每一列刚好2个1.
r%3 == 1, 只需要考虑最后一行,使得左边的1尽可能的多,循环的3行里面满足要求的只有第一行,110110110,这样左边,然后答案也很明显,就是 r / 3 * c * 2 + c - c / 3;
r%3 == 2,也是考虑最后2行,使得左边的1尽可能的多,然后就有2种组合:
110110110110
101101101101
或者:
110110110110
011011011011
显然,上面那种情况比较优, 答案就是 r / 3 * c * 2 + c + (c + 2) / 3.
然后,就是这样。
第三题
1. 遇到这题,很容易想到的一个解法就是在当前位置,枚举接下来可能出现的单词,从前往后枚举,如果所有字符都是单词,则结果+1,可以把每个字符出现过的单词链接起来,加速枚举过程,小数据,就这样很容过了,但是大数据,通过 一个一个数的方式,大数据是过不了的,还得想其他的办法。
2.还是动态规划,以单词长度为一维,考虑到该位置的所有可能数,考虑这个数怎么计算。我的想法是依次枚举每个单词,看这个单词能不能出现,然后结果就是dp[i] = dp[i] + dp[i - size(word)]。这里每一个word都要判断。最后的结果就是dp[size(sentence)]。计算一下复杂度,4000 * 400 * 20 = 32000000,可以在时限内完成。
贴一下代码:
const int mod = 1e9 + ;
vector<string> s;
int dp[];
int n, m;
void solve() {
cin >> n >> m;
s.clear(); string t;
for (int i = ; i < n; i++) {
cin >> t; s.pb(t);
}
while(m--) {
cin >> t;
int len = t.size();
memset(dp, , sizeof dp);
dp[] = ;
for (int i = ; i <= len; i++) {
for (int j = ; j < n; j++) {
vector<int> v(, );
if(i >= s[j].size()) {
for (int x = ; x < s[j].size(); x++) {
v[t[i - x - ] - 'a']++;
v[s[j][x] - 'a' ]--;
}
bool f = ;
for (int x = ; x < ; x++) {
if(v[x] != ) {f = ;
break;
}
}
if(f) {
dp[i] = (dp[i] + dp[i - s[j].size() ]) % mod;
}
}
}
}
cout << ' ' << dp[len];
}
}
这里可以考虑加速过程,2个单词,如果他们含有的字母以及个数相同,虽然顺序不同,但是可以算作一个单词,可以通过长度26的数组,算出一个值,统计每个字符的个数,算作这个word的hash值,这样复杂度就可以缩小一个因子,通过map来统计这样处理后相同的单词的个数来做,由于每个单词长度最多是20,所以复杂度就是4000 * 20,map查找的过程可以认为是O(1),下面贴一下第一名大神的代码。
#include <cstdio>
#include <map>
#include <vector> using namespace std; const int N = ;
const int mod = ; map<vector<int>, int> c;
char s[N];
int dp[N];
int n, m; inline void update(int &a, long long b) {
a = (a + b) % mod;
} int main() {
int t, tt;
scanf("%d", &t);
for (tt = ; tt <= t; tt++) {
scanf("%d%d", &n, &m);
c.clear();
for (int i = ; i < n; i++) {
vector<int> a();
scanf("%s", s);
for (int j = ; s[j] != '\0'; j++) a[s[j] - 'a']++;
c[a]++;
}
printf("Case #%d:", tt);
for (int i = ; i < m; i++) {
scanf("%s", s);
int ls = strlen(s);
dp[] = ;
for (int j = ; j < ls; j++) {
dp[j + ] = ;
vector<int> a();
for (int k = ; k < && j - k >= ; k++) {
a[s[j - k] - 'a']++;
map<vector<int>, int>::iterator it = c.find(a);
if (it != c.end()) update(dp[j + ], (long long)dp[j - k] * it->second);
}
}
printf(" %d", dp[ls]);
}
printf("\n");
}
return ;
}
这里我第一次看见vector也可以作为map键值的代码,真是开阔眼界了。学习一下。其实这道题,就跟普通的dp套路一样,总长度一般作为一维,枚举最后一个单词是什么,然后加上前面的长度的结果,而这些结果已经提前计算出来了,然后就可以递推了,这个套路很一般,如果一眼能看到,应该很容易ac,码代码难度也不是很大。
第四题
1.看题,看到区间,价值,单个区间可能比几个区间加起来的价值还小,所以贪心是不行的,然后我看到小数据范围是10,2^10=1024,简单的可以枚举所有情况,所以小数据就暴力,很简单的就过了!接下来考虑大数据,2^1000,去枚举肯定是不行的,需要寻找其他路径。
2.这也是dp,这次dp的题怎么这么多,(可以总结一下一般的套路,dp(计数,概率),二分(比较难的二分,最大最小),位操作,加上一些stack,queue的题目,还有什么以后再加),看别人的答案,有需要线段树的,其实是维护滑动窗口内的最小值,这个可以通过deque,速度非常快,上次也是apac也做到,专门考这个知识点的,那个题比较水,因为一眼可以看出来是考哪个知识点的,所以很容易想到,这道题目,不仔细分析,想不到用这个吧。
双端队列维护滑动窗口内的最大值或者最小值的题目:https://code.google.com/codejam/contest/4284487/dashboard#s=p3就是这个,维护窗口内最大值,是二维的,可以轻松的转化到一维的情况。
这个题目,其实一开始可以考虑用dp,因为一般最小值,然后是加起来,还有方案数,各种组合,跟具体的组合无关的,一般是dp,这还是最后一题,dp的可能性最大。然后是以输入的区间个数为一维,模仿背包的过程,每来一个区间段,对所有可能的结果进行更新。
3. 然后就想到第二维是结果的可能哪一位,最后求长度L的最小值,然后小于这个长度需要先计算出来,每来一个新的区间段,看这个区间段可以对那些长度进行更新,考虑是0/1背包,可以对长度从大到小更新,从而可以省去线段个数这一维,也是dp的第一维。考虑这样的转移方式,dp[i] = min(dp[i], min(dp[i - r], dp[i - l]) + c), 当前的输入区间是(l, r), 花费是 c, 接下来需要考虑的就是如何高效的维护min(dp[i - r], dp[i - l]) , 对于从1~L的区间每一个点都需要更新,(这里dp[0] = 0,这是初始化条件,不需要更新,)每一个都需要前面的一个区间的最小值来更新, 这个区间是固定大小的,滑动的,可以通过上面的deque来维护, 或者可以同线段树来维护区间的最小值,复杂度是O(logn),单点更新,(这里不需要区间更新,线段树写起来还是比较简单的)。
下面的过程都很明朗了,直接码代码就可以了!
先是线段树代码,可以使用线段树模板。(下面是排名第二的大神的思路,我用自己熟悉的线段树写的)。
#include<bits/stdc++.h>
#define pb push_back
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
#define dbg(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl
typedef long long ll;
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e4 + ;
const ll inf = 1ll << ;
ll f[maxn * ];
int x, y;
ll v;
void bt(int o, int l, int r) {
if(l == r) {
f[o] = 1ll << ;
} else {
int mid = (l + r) / ;
bt(o * , l, mid);
bt(o * + , mid + , r);
f[o] = min(f[o * ], f[o * + ]);
}
}
void update(int o, int l, int r) {
if(l == r && l == x) {
f[o] = min(f[o], v);
} else {
int mid = (l + r) / ;
if(x <= mid) update(o * , l, mid);
else update(o * + , mid + , r);
f[o] = min(f[o * ], f[o * + ]);
}
}
ll ask(int o, int l, int r) {
if(r < x || l > y) return 1ll << ;
if(x <= l && r <= y) return f[o];
int mid = (l + r) / ;
ll res = 1ll << ;
if(x <= mid) res = min(res, ask(o * , l, mid));
if(y > mid) res = min(res, ask(o * + , mid + , r));
return res;
}
int n, m, l;
void solve() {
cin >> n >> m >> l;
bt(, , l);
x = , v = ;
update(, , l);
int tl, tr, p;
for (int i = ; i <= n; i++) {
cin >> tl >> tr >> p;
for (int j = l; j >= ; j--) {
int cl = max(, j - tr);
int cr = min(j - , j - tl);
if(cl <= cr) {
x = cl, y = cr;
ll t = ask(, , l);
//cout << i << " " << j << " " << cl << " " << cr << " " << t << endl;
x = j; v = t + p;
update(, , l);
}
}
}
x = y = l;
ll res = ask(, , l);
if(res <= m) {
cout << res << endl;
} else {
cout << "IMPOSSIBLE" << endl;
}
}
int main() {
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
int _; cin >> _;
for (int i = ; i <= _; i++) {
cout << "Case #" << i << ": ";
solve();
} return ;
}
接着是deque代码,这个是排名第一的大神的代码,写的非常漂亮。
#include <cstdio>
#include <algorithm> using namespace std; const int N = ;
const long long INF = ~0ull >> ; long long dp[N];
int n, m, l;
int d[N], p, q; int main() {
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
int t, tt;
scanf("%d", &t);
for (tt = ; tt <= t; tt++) {
for (int i = ; i < N; i++) dp[i] = INF;
dp[] = ;
scanf("%d%d%d", &n, &m, &l);
for (int i = ; i < n; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
p = q = ;
for (int j = a; l - j >= && j < b; j++) {
while (q > p && dp[d[q - ]] >= dp[l - j]) q--;
d[q++] = l - j;
}
for (int j = l; j >= ; j--) {
if (j - b >= ) {
while (q > p && dp[d[q - ]] >= dp[j - b]) q--;
d[q++] = j - b;
}
if (p < q) dp[j] = min(dp[j], dp[d[p]] + c);
if (d[p] >= j - a) p++;
}
}
if (dp[l] <= m) printf("Case #%d: %lld\n", tt, dp[l]);
else printf("Case #%d: IMPOSSIBLE\n", tt);
}
return ;
}
总结:
这次4道题,3道dp,一道找规律,如果有一些套路,加上简单思考应该可以做出来吧!看了这些大神的代码,恍然大悟,原来是这样,需要一些小的知识点,比如上面第四题(这些知识通过平时的积累),还有遇到问题怎么分析,怎么分析,就是套路,多做题练习和思考,多掌握一些套路,以后总会有帮助的。
2017 google Round D APAC Test 题解的更多相关文章
- 2017 google Round C APAC Test 题解
题解参考网上的答案,以及我自己的想法. 主要参考网站:http://codeforces.com/blog/entry/47181,http://codeforces.com/blog/entry/4 ...
- 喵哈哈村的魔法考试 Round #1 (Div.2) 题解
喵哈哈村的魔法考试 Round #1 (Div.2) 题解 特别感谢出题人,qscqesze. 也特别感谢测题人Xiper和CS_LYJ1997. 没有他们的付出,就不会有这场比赛. A 喵哈哈村的魔 ...
- Codeforces Round #182 (Div. 1)题解【ABCD】
Codeforces Round #182 (Div. 1)题解 A题:Yaroslav and Sequence1 题意: 给你\(2*n+1\)个元素,你每次可以进行无数种操作,每次操作必须选择其 ...
- 喵哈哈村的魔法考试 Round #2 (Div.2) 题解
喵哈哈村的魔法考试 Round #2 (Div.2) 题解 A.喵哈哈村的战争 题解: 这道题就是for一遍,统计每个村子的战斗力的和,然后统计哪个村子的战斗力和大一点就好了. 唯一的坑点,就是这道题 ...
- 中国2017 Google 开发者大会第一天简单回顾
昨天有幸参加了中国2017 Google 开发者大会,在这第一天就收获满满,昨天太忙了,今天早晨来一起简单回顾一下,可以让没有参加的童鞋们感受一下现场的温度. 早早就来到了会议现场,外面看不出什么特别 ...
- Codeforces Round #608 (Div. 2) 题解
目录 Codeforces Round #608 (Div. 2) 题解 前言 A. Suits 题意 做法 程序 B. Blocks 题意 做法 程序 C. Shawarma Tent 题意 做法 ...
- Codeforces Round #525 (Div. 2)题解
Codeforces Round #525 (Div. 2)题解 题解 CF1088A [Ehab and another construction problem] 依据题意枚举即可 # inclu ...
- Codeforces Round #528 (Div. 2)题解
Codeforces Round #528 (Div. 2)题解 A. Right-Left Cipher 很明显这道题按题意逆序解码即可 Code: # include <bits/stdc+ ...
- Codeforces Round #466 (Div. 2) 题解940A 940B 940C 940D 940E 940F
Codeforces Round #466 (Div. 2) 题解 A.Points on the line 题目大意: 给你一个数列,定义数列的权值为最大值减去最小值,问最少删除几个数,使得数列的权 ...
随机推荐
- Sql Server 带参数的存储过程执行方法
Sql Server 带参数的存储过程执行方法 Visual C# 动态操作 SQL Server 数据库实例教程(4):带参数的存储过程执行方法 上一篇文章介绍了带参数的SQL语句执行方法和不带参数 ...
- PowerDesigner 物理数据模型(PDM)
PowerDesigner 物理数据模型(PDM) 说明 数据库脚本sqldatabasegeneration存储 目录(?)[+] 一. PDM 介绍 物理数据模型(Physical ...
- SQLite使用教程8 Insert 语句
http://www.runoob.com/sqlite/sqlite-insert.html SQLite Insert 语句 SQLite 的 INSERT INTO 语句用于向数据库的某个表中添 ...
- CentOS下系统时间同步和时区的修改和设置(用的这个)
一.修正时区 rm -rf /etc/localtime #删除当前默认时区www.kwx.gd ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localt ...
- ThinkPHP CURD方法盘点:field方法
ThinkPHP的CURD操作中有很多非常实用的方法,从这篇开始,我们会为大家一一介绍. 首先为大家介绍下field方法的用法.field属于模型的连贯操作方法之一,主要目的是标识要返回或者操作的字段 ...
- zookeeper配置同步zookeeper编程
分布式助手Zookeeper(四) kissyoudyb 2013-12-05 17:41 阅读:33 评论:0 分布式助手Zookeeper(三) kissyoudyb 2013-12-05 ...
- yum、RPM常用的命令(转)
# yum install xxx 安装xxx软件# yum info xxx 查看xxx软件的信息# yum remove xxx ...
- Memcached source code analysis (threading model)--reference
Look under the start memcahced threading process memcached multi-threaded mainly by instantiating mu ...
- Helpers Overview
Helpers Overview Helpers are classes that are not part of the core system but can greatly improve it ...
- 导入MyEclipse项目乱码
1.右键项目属性→text file encoding →修改为UTF-8,即可自动变成正常的.