这次算是成绩最好的一次了,笔试结束后仍然可以练习,地址戳https://code.google.com/codejam/contest/10214486/dashboard。
A. Travel
一看就是图论当中最短路的变形,但是边的权重会随着时间发生变化。对于dijkstra或者Bellman Ford之类的最短路算法,它们本质上都是动态规划,需要满足最优子结构性质,如果边权重随着时间的变化是不规则的,那么这些算法都无法保证得到最优解。
但是题目中有一个条件: cost(i)≤cost(i+1)+1 ,换句话说,不可能出现从一个顶点晚出发却早到终点的情况,于是不管是dijkstra, bellman ford, spfa都是仍然适用的,预先计算出1在所有时间点到所有点的最短路, O(1) 即可回答每一个查询。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> point;
const int INF = INT_MAX / 2;
vector<int> cost[505][505];
void solve() {
int N, M, K, x, y, c;
scanf("%d %d %d", &N, &M, &K);
FOR(i, N + 1) FOR(j, N + 1) cost[i][j].clear();
FOR(i, M) {
scanf("%d %d", &x, &y);
FOR(j, 24) {
scanf("%d", &c);
cost[x][y].push_back(c);
cost[y][x].push_back(c);
}
}
int dis[505][24];
FOR(i, N + 1) FOR(j, 24) dis[i][j] = INF;
FOR(start, 24) {
// spfa
vector<bool> inq(N + 1, false);
dis[1][start] = 0; inq[1] = true;
queue<int> q;
q.push(1);
while (!q.empty()) {
int tp = q.front(); q.pop(); inq[tp] = false;
for (int i = 1; i <= N; ++i) {
if (cost[tp][i].empty()) continue;
int md = (start + dis[tp][start]) % 24;
if (dis[i][start] > dis[tp][start] + cost[tp][i][md]) {
dis[i][start] = dis[tp][start] + cost[tp][i][md];
if (!inq[i]) { q.push(i); inq[i] = true; }
}
}
}
}
int D, S;
FOR(i, K) {
scanf("%d %d", &D, &S);
if (dis[D][S] == INF) {
cout << " -1";
}
else cout << " " << dis[D][S];
}
cout << endl;
return;
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ":";
solve();
}
return 0;
}
B.gWheels
我读了半天题才看明白什么意思。。。说白了就是有三个数组 a[1],a[2]...a[Np] , b[1],b[2]...b[Ne] , c[1],c[2]...c[Nt] ,然后要求从 a,c 数组中分别选择一个元素,从 b 数组中选择两个不同的元素,满足以下约束条件:
对于小数据暴力枚举就好了,对于大数据,可以预先计算出所有的 br/bl ,存到set里面,然后只枚举 a,c 数组,去set里面查找是否有满足要求的分数。下面的代码在大数据上跑了大概1分半种,可以满足8分钟以内的要求。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
void solve() {
int np, ne, nt;
scanf("%d %d %d", &np, &ne, &nt);
vector<ll> gp(np), ge(ne), gt(nt);
FOR(i, np) scanf("%lld", &gp[i]);
FOR(i, ne) scanf("%lld", &ge[i]);
FOR(i, nt) scanf("%lld", >[i]);
set<pll> extra;
FOR(i, ne) for (int j = i + 1; j < ne; ++j) {
ll g = __gcd(ge[i], ge[j]);
ll a = ge[i] / g, b = ge[j] / g;
extra.insert(make_pair(a, b));
extra.insert(make_pair(b, a));
}
set<pll> base;
FOR(i, np) FOR(j, nt) {
ll g = __gcd(gp[i], gt[j]);
ll a = gp[i] / g, b = gt[j] / g;
base.insert(make_pair(a, b));
}
int M;
ll P, Q;
scanf("%d", &M);
set<pll>::iterator it;
FOR(i, M) {
bool ok = false;
scanf("%lld %lld", &P, &Q);
for (it = extra.begin(); it != extra.end(); ++it) {
ll a = P * ((*it).first), b = Q * ((*it).second);
ll g = __gcd(a, b);
a /= g;
b /= g;
if (base.find(make_pair(a, b)) != base.end()) {
ok = true;
break;
}
}
if (ok) printf("Yes\n");
else printf("No\n");
}
return;
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ":" << endl;
solve();
}
return 0;
}
C. gNumbers
这是一个博弈论 + 数论的题目,稍微分析一下就能发现其实是个很常见的博弈题目,A(先手)必胜当且仅当有一种选择质因子的办法使得B(后手)必败,B必败当且仅当所有选择质因子的办法都使得A必胜。递归去求解就行了。
对于大数据, n≤1015 ,尽管如此,n的质因子个数也不会超过15个,因此递归的层数不多,完全是在时间接受范围内的,下面的代码在大数据上跑了不到2s。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> point;
const char* FIRST = "Laurence";
const char* SECOND = "Seymour";
bool lose(ll num);
bool win(ll num);
vector<ll> factors;
inline bool isprime(int num) {
assert(num >= 2);
for (int i = 2; i * i <= num; ++i) {
if (num % i == 0) return false;
}
return true;
}
inline bool gprime(ll num) {
int cnt = 0;
while (num > 0) {
cnt += (num % 10);
num /= 10;
}
return ((cnt == 1) || isprime(cnt));
}
bool lose(ll num) {
if (gprime(num)) return true;
FOR(i, factors.size()) {
if (num % factors[i] == 0) {
ll tmp = num;
while (tmp % factors[i] == 0) tmp /= factors[i];
if (!win(tmp)) return false;
}
}
return true;
}
bool win(ll num) {
if (gprime(num)) return false;
FOR(i, factors.size()) {
if (num % factors[i] == 0) {
ll tmp = num;
while (tmp % factors[i] == 0) tmp /= factors[i];
if (lose(tmp)) return true;
}
}
return false;
}
void solve() {
ll n;
cin >> n;
factors.clear();
ll tmp = n;
for (ll i = 2; i * i <= tmp; ++i) {
if (tmp % i == 0) {
factors.push_back(i);
while (tmp % i == 0) tmp /= i;
}
}
if (tmp > 1) factors.push_back(tmp);
if (win(n)) printf("%s\n", FIRST);
else printf("%s\n", SECOND);
return;
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ": ";
solve();
}
return 0;
}
D. Albocede DNA
我比赛的时候只搞出来了小数据,也是动态规划,枚举所有可能的valid sequence,但复杂度很高。
后来膜拜了kcm1700大神的代码,很简洁清晰。。。设置状态 dp[i][j][k] 表示当前已经在处理第 i 类基因( 0≤i≤3 ,分别表示a, b, c, d),还剩下j个a,k个b需要处理,根据题目约束, 0≤j,k<250 。这样状态转移方程如下:
// by kcm1700
#include <cstdio>
#include <climits>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <string>
#include <set>
#include <deque>
#include <thread>
using namespace std;
const int mod = 1000000007;
long long dp[2][4][252][252];
char dat[512];
void add(long long &dest, long long val)
{
dest = (dest+val) % mod;
}
int main()
{
int T;
scanf("%d",&T);
for (int testcase = 1; testcase <= T; testcase++)
{
fprintf(stderr, "Case #%d processing\n", testcase);
scanf("%s",dat);
int n = strlen(dat);
long long ans = 0;
memset(dp,0,sizeof(dp));
dp[0][0][0][0] = 1;
for (int i = 0; i < n; i++) {
int ci = i&1;
int ni = !ci;
memcpy(dp[ni], dp[ci], sizeof(dp[ci]));
for (int state = 0; state < 4; state++) {
for (int c1 = 0; c1 <= 250; c1++) {
for (int c2 = 0; c2 <= 250; c2++) {
auto cur =dp[ci][state][c1][c2];
if (cur==0)continue;
if (dat[i] == 'a') {
if (state == 0) {
add(dp[ni][state][c1+1][c2], cur);
}
} else if (dat[i] == 'b') {
if (state == 0) {
add(dp[ni][1][c1][1], cur);
} else if (state == 1) {
add(dp[ni][1][c1][c2+1], cur);
}
} else if (dat[i] == 'c') {
if (state == 1) {
if (c1 >= 2) {
add(dp[ni][2][c1-1][c2],cur);
} else if (c1 == 1) {
add(dp[ni][3][c1-1][c2],cur);
}
} else if (state == 2) {
if (c1 >= 2) {
add(dp[ni][2][c1-1][c2],cur);
} else if (c1 == 1) {
add(dp[ni][3][c1-1][c2],cur);
}
}
} else {
if (state == 3) {
if (c2 >= 2) {
add(dp[ni][3][c1][c2-1],cur);
} else if (c2 == 1) {
add(dp[ni][0][0][0],cur);
add(ans, cur);
}
}
}
}
}
}
}
ans = (ans%mod+mod)%mod;
printf("Case #%d: %lld\n",testcase, ans);
}
return 0;
}
总体来说,Round B比Round A要难了一点,不过也还算基本。。