A. Shell Game(Codeforces 777A)
思路
爲了更好地找到解題關鍵,先將小球隨杯子運動的軌跡畫在紙上。具體地,假設初始狀態球在杯子
於是我們用二維數組
代碼
#include <bits/stdc++.h>
using namespace std;
int n, x;
int d[3][6] = { {0, 1, 2, 2, 1, 0},
{1, 0, 0, 1, 2, 2},
{2, 2, 1, 0, 0, 1} };
int main() {
// freopen("data.txt", "r", stdin);
cin >> n >> x;
n = n % 6;
for(int i = 0; i < 3; i++) {
if(d[i][n] == x) {
cout << i << endl;
break;
}
}
return 0;
}
B. Game of Credit Cards(Codeforces 777B)
思路
首先這個數據規模暴力肯定是不行了。那麼潛在的,可能有貪心策略也可能是動態規劃。那麼先考察有沒有貪心策略。因爲兩個字符串的序對本題是沒有影響的(因爲M已經知道S的出牌順序)。因此考慮對兩個字符串進行排序。我們分兩種策略來考慮:
- 被
S 攻擊的最少次數。假設我們是M ,那麼我們的目標是讓對方的牌儘可能不發揮作用。我們從當前S 的點數最大的S[i] 這兒考慮。爲了讓S[i] 啞火,我們可以拿出最大的M[j] 來壓制它。這樣爲什麼是最好的呢?因爲如果M[j] 都壓不住它,那麼也沒有牌能夠壓住它了。況且如果此時不用M[j] ,有什麼理由以後再用它呢?有人可能會反駁:能不能放棄壓制S[i] ,把M[j] 留到後面用呢?假如這樣做,那麼最好的情況是結果不變,最差的情況是結果變大了(也就是次優解)。因此我們按照點數從大到小枚舉S[i] ,用當前最大的牌來壓制對方即可。 - 攻擊
S 的最多次數。假設我們是M ,那我們的目標是讓我們的牌儘可能發揮作用。那麼我們從M 的點數最大的M[j] 這兒考慮。爲了讓M[j] 發揮最大效果,我們選擇比M[j] 小的(或相等的)最大的S[i] 下手。因爲對於任意滿足S[k]≤S[i] 的k ,都存在S[k]≤M[j] ,那麼將M[j] 用在S[i] 上最利於我方的後來的牌的發揮。我們同樣可以從大到小枚舉S[j] ,用當前最大的牌來壓制對方。
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
char S[maxn], M[maxn];
int n, Max, Min;
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d%s%s", &n, S, M);
sort(S, S + n);
sort(M, M + n);
int j = n - 1;
for(int i = n - 1; i >= 0; i--) {
if(S[i] > M[j]) {
Min++;
}
else {
j--;
}
}
j = n - 1;
for(int i = n - 1; i >= 0; i--) {
if(S[i] < M[j]) {
Max++;
j--;
}
}
printf("%d\n%d\n", Min, Max);
return 0;
}
C. Alyona and Spreadsheet(Codeforces 777C)
思路
對於這題,不少人都能夠立即想到暴力的解法:對於每列
事實上,這種要對矩陣進行某種統計,暴力解複雜度太大的題,多半是要維護某種神奇的量,使得我們只用枚舉行或枚舉列就能完成這種統計。話說
那麼這題怎麼樣呢?我們可以事先統計
對於第
不等式左邊的式子表示以
是否存在一個
顯然這個問題就是在問
那麼對於每個
最後,實現上需要注意一個細節。因爲題目只說
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
vector <int> a[maxn], d[maxn];
int n, m, k, l, r, num, Max[maxn];
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d%d", &n, &m);
// 輸入並存儲矩陣
for(int i = 1; i <= n; i++) {
a[i].push_back(0);
for(int j = 1; j <= m; j++) {
scanf("%d", &num);
a[i].push_back(num);
}
}
for(int j = 1; j <= m; j++) {
d[j].push_back(0);
d[j].push_back(1);
}
// 對每列計算以某個元素結尾的最長不下降子串
for(int j = 1; j <= m; j++) {
for(int i = 2; i <= n; i++) {
if(a[i][j] >= a[i-1][j]) {
d[j].push_back(d[j][i-1] + 1);
}
else {
d[j].push_back(1);
}
}
}
// 統計每行的列最長不下降子串的最大值
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
Max[i] = max(Max[i], d[j][i]);
}
}
// 回答查詢
scanf("%d", &k);
while(k--) {
scanf("%d%d", &l, &r);
puts(r - Max[r] + 1 <= l ? "Yes" : "No");
}
return 0;
}
D. Cloud of Hashtags(Codeforces 777D)
思路
這題從數據規模和“字典序”這個這麼強的條件來看,可能存在貪心策略。首先從先往後考慮,結果是沒什麼結果。那麼從後往前考慮呢?首先,對於一個字符串,我們刪除它的後綴只會讓它的字典序變小而不是變大。那麼,對於倒數第一個串
從這個靈感不難得出貪心算法:我們從第一位開始逐位比較兩個字符串
- 如果出現某位j使得
s[i][j]>s[i−1][j] ,那麼一切順利。 - 如果出現某位j使得
s[i][j]<s[i−1][j] ,那麼從j 開始的s[i−1] 的後綴要全部刪除掉。 - 如果一直是
s[i][j]==s[i−1][j] 直到某個串被遍歷完,那麼只需要s[i] 的長度大於或等於s[i−1] 的長度即可。
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
string s[maxn];
bool equ;
int n, p1, p2;
int main() {
// freopen("data.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> s[i];
}
for(int i = n - 1; i >= 1; i--) {
equ = true;
p1 = p2 = 0;
while(true) {
if(s[i+1][p1] < s[i][p2]) {
if(true == equ) {
s[i] = s[i].substr(0, p2);
}
break;
}
if(s[i+1][p1] > s[i][p2]) {
equ = false;
}
if(++p2 >= s[i].size()) {
break;
}
if(++p1 >= s[i+1].size()) {
if(true == equ) {
s[i] = s[i].substr(0, p2);
}
break;
}
}
}
for(int i = 1; i <= n; i++) {
cout << s[i] << endl;
}
return 0;
}
E. Hanoi Factory(Codeforces 777E)
思路
這題顯然不是貪心能夠解決的(很難找到某個順序進行某種貪心策略)。考慮動態規劃。我們需要有一個“序”來讓動規滿足“無後效性”。根據題目的特點先按照外徑從大到小對
令狀態
這看上去會是一個
代碼
#include <bits/stdc++.h>
using namespace std;
#define lch (k << 1)
#define rch (k << 1 | 1)
#define mid ((l + r) >> 1)
typedef long long ll;
const int maxn = 2e5 + 10;
map <int, int> mp;
int n, a, b, h, m, foo[maxn], bar[maxn];
ll d, tmp, ans;
// 排序用的ring結構體
struct ring {
int a, b, h;
ring() {}
ring(int a, int b, int h):a(a), b(b), h(h) {}
bool operator < (const ring& o) const {
if(b == o.b) {
return a > o.a;
}
return b > o.b;
}
}rings[maxn];
// 線段樹
template <class T>
struct Tree {
T data[maxn<<2];
T operate(T x, T y) {
return max(x, y);
}
void pushUp(int k) {
data[k] = operate(data[lch], data[rch]);
}
// 建樹
void build(int k, int l, int r) {
if(l == r) {
data[k] = 0;
return;
}
build(lch, l, mid);
build(rch, mid + 1, r);
pushUp(k);
}
// 修改
void update(int a, T v, int k, int l, int r) {
if(l == r) {
data[k] = v;
return;
}
if(a <= mid) {
update(a, v, lch, l, mid);
}
else {
update(a, v, rch, mid + 1, r);
}
pushUp(k);
}
// 查詢
T query(int a, int b, int k, int l, int r) {
if(a <= l && r <= b) {
return data[k];
}
ll res = 0;
if(a <= mid) {
res = operate(res, query(a, b, lch, l, mid));
}
if(b > mid) {
res = operate(res, query(a, b, rch, mid + 1, r));
}
return res;
}
};
Tree <ll> o;
// 離散化
int dec(int a[], int b[], int n, map <int, int>& mp) {
copy(a + 1, a + n + 1, b + 1);
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - b - 1;
for(int i = 1; i <= n; i++) {
mp[a[i]] = lower_bound(b + 1, b + m + 1, a[i]) - b;
}
return m;
}
int main() {
// freopen("data.txt", "r", stdin);
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d%d", &a, &b, &h);
rings[i] = ring(a, b, h);
foo[i] = a;
foo[n + i] = b;
}
// 離散化
m = dec(foo, bar, 2 * n, mp);
o.build(1, 1, m);
sort(rings + 1, rings + n + 1);
for(int i = 1; i <= n; i++) {
a = rings[i].a;
b = rings[i].b;
h = rings[i].h;
// 查詢d[j]
if(mp[b] >= 2) {
d = o.query(1, mp[b] - 1, 1, 1, m) + h;
}
else {
d = h;
}
// tmp相當於d[i]
tmp = o.query(mp[a], mp[a], 1, 1, m);
// 將d[i]插入線段樹
if(d > tmp) {
o.update(mp[a], d, 1, 1, m);
}
// 用d[i]更新答案
ans = max(ans, d);
}
printf("%I64d\n", ans);
return 0;
}