Google APAC 2016 University Graduates Test Round B解題報告
先傳個筆試成績的截圖:
一看就是圖論當中最短路的變形,但是邊的權重會隨著時間發生變化。對於dijkstra或者Bellman Ford之類的最短路演算法,它們本質上都是動態規劃,需要滿足最優子結構性質,如果邊權重隨著時間的變化是不規則的,那麼這些演算法都無法保證得到最優解。
但是題目中有一個條件:
#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;
}
我讀了半天題才看明白什麼意思。。。說白了就是有三個陣列
對於小資料暴力列舉就好了,對於大資料,可以預先計算出所有的
#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;
}
這是一個博弈論 + 數論的題目,稍微分析一下就能發現其實是個很常見的博弈題目,A(先手)必勝當且僅當有一種選擇質因子的辦法使得B(後手)必敗,B必敗當且僅當所有選擇質因子的辦法都使得A必勝。遞迴去求解就行了。
對於大資料,
#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;
}
我比賽的時候只搞出來了小資料,也是動態規劃,列舉所有可能的valid sequence,但複雜度很高。
後來膜拜了kcm1700大神的程式碼,很簡潔清晰。。。設定狀態
還有其他一些狀態之間轉換的方程,類似推導一下就行,kcm1700大神的程式碼裡寫得很清楚,自己看就好了。。
// 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要難了一點,不過也還算基本。。