首先说明一下:我只是用暴力过了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 题解的更多相关文章

  1. 2017 google Round C APAC Test 题解

    题解参考网上的答案,以及我自己的想法. 主要参考网站:http://codeforces.com/blog/entry/47181,http://codeforces.com/blog/entry/4 ...

  2. 喵哈哈村的魔法考试 Round #1 (Div.2) 题解

    喵哈哈村的魔法考试 Round #1 (Div.2) 题解 特别感谢出题人,qscqesze. 也特别感谢测题人Xiper和CS_LYJ1997. 没有他们的付出,就不会有这场比赛. A 喵哈哈村的魔 ...

  3. Codeforces Round #182 (Div. 1)题解【ABCD】

    Codeforces Round #182 (Div. 1)题解 A题:Yaroslav and Sequence1 题意: 给你\(2*n+1\)个元素,你每次可以进行无数种操作,每次操作必须选择其 ...

  4. 喵哈哈村的魔法考试 Round #2 (Div.2) 题解

    喵哈哈村的魔法考试 Round #2 (Div.2) 题解 A.喵哈哈村的战争 题解: 这道题就是for一遍,统计每个村子的战斗力的和,然后统计哪个村子的战斗力和大一点就好了. 唯一的坑点,就是这道题 ...

  5. 中国2017 Google 开发者大会第一天简单回顾

    昨天有幸参加了中国2017 Google 开发者大会,在这第一天就收获满满,昨天太忙了,今天早晨来一起简单回顾一下,可以让没有参加的童鞋们感受一下现场的温度. 早早就来到了会议现场,外面看不出什么特别 ...

  6. Codeforces Round #608 (Div. 2) 题解

    目录 Codeforces Round #608 (Div. 2) 题解 前言 A. Suits 题意 做法 程序 B. Blocks 题意 做法 程序 C. Shawarma Tent 题意 做法 ...

  7. Codeforces Round #525 (Div. 2)题解

    Codeforces Round #525 (Div. 2)题解 题解 CF1088A [Ehab and another construction problem] 依据题意枚举即可 # inclu ...

  8. Codeforces Round #528 (Div. 2)题解

    Codeforces Round #528 (Div. 2)题解 A. Right-Left Cipher 很明显这道题按题意逆序解码即可 Code: # include <bits/stdc+ ...

  9. Codeforces Round #466 (Div. 2) 题解940A 940B 940C 940D 940E 940F

    Codeforces Round #466 (Div. 2) 题解 A.Points on the line 题目大意: 给你一个数列,定义数列的权值为最大值减去最小值,问最少删除几个数,使得数列的权 ...

随机推荐

  1. Sql Server 带参数的存储过程执行方法

    Sql Server 带参数的存储过程执行方法 Visual C# 动态操作 SQL Server 数据库实例教程(4):带参数的存储过程执行方法 上一篇文章介绍了带参数的SQL语句执行方法和不带参数 ...

  2. PowerDesigner 物理数据模型(PDM)

    PowerDesigner 物理数据模型(PDM) 说明 数据库脚本sqldatabasegeneration存储   目录(?)[+]   一.     PDM 介绍 物理数据模型(Physical ...

  3. SQLite使用教程8 Insert 语句

    http://www.runoob.com/sqlite/sqlite-insert.html SQLite Insert 语句 SQLite 的 INSERT INTO 语句用于向数据库的某个表中添 ...

  4. CentOS下系统时间同步和时区的修改和设置(用的这个)

    一.修正时区 rm -rf /etc/localtime #删除当前默认时区www.kwx.gd ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localt ...

  5. ThinkPHP CURD方法盘点:field方法

    ThinkPHP的CURD操作中有很多非常实用的方法,从这篇开始,我们会为大家一一介绍. 首先为大家介绍下field方法的用法.field属于模型的连贯操作方法之一,主要目的是标识要返回或者操作的字段 ...

  6. zookeeper配置同步zookeeper编程

    分布式助手Zookeeper(四) kissyoudyb 2013-12-05 17:41 阅读:33 评论:0     分布式助手Zookeeper(三) kissyoudyb 2013-12-05 ...

  7. yum、RPM常用的命令(转)

    # yum install xxx            安装xxx软件# yum info xxx                查看xxx软件的信息# yum remove xxx         ...

  8. Memcached source code analysis (threading model)--reference

    Look under the start memcahced threading process memcached multi-threaded mainly by instantiating mu ...

  9. Helpers Overview

    Helpers Overview Helpers are classes that are not part of the core system but can greatly improve it ...

  10. 导入MyEclipse项目乱码

    1.右键项目属性→text file encoding →修改为UTF-8,即可自动变成正常的.