Codeforces Round #530 (Div. 2) + 二分

Codeforces鏈接 :http://codeforces.com/contest/1099

A. Snowball(模擬)

  題意 :現在有一個高爲 hh,重量爲 ww 的一個雪球,每一秒鐘都會發生一些事情,雪球的重量會增加 hh,如果當前高度到達了某一個石頭的高度那麼它就會撞擊這個石頭,此時雪球的重量相應的減少一定的重量,減少的重量就是這個石頭的重量,然後這個雪球的高度下降 1,等到雪球的高度下降到 0 的時候,雪球就會停止滾動,如果在滾動的過程中雪球的重量達到了負值,那麼雪球的重量就視爲 0。問在雪球停止滾動的時候雪球的重量是多少?
  思路 :根據題目的描述,我們以高度最爲參考來模擬一下這個過程即可。高度爲 0 時輸出最終結果即可。

#include<bits/stdc++.h>
using namespace std;

int main() {
	int w, h; scanf("%d%d", &w, &h);
	int w1, h1, w2, h2; scanf("%d%d%d%d", &w1, &h1, &w2, &h2);
	while (h) {
		w += h;
		if (h == h1) w -= w1;
		if (w < 0) w = 0;
		if (h == h2) w -= w2;
		if (w < 0) w = 0;
		-- h;
	}
	printf("%d\n", w);
	return 0;
}

B. Squares and Segments(枚舉)

  題意 :SofiaSofia 想要畫一些正方形,但是她需要畫出來一些邊進行參考, 剩下的需要的邊是從這些邊平移出來的。現在給出 SofiaSofia 想要畫出來的正方形的個數,問剛開始 SofiaSofia 最少需要畫出多少條邊?
  思路 :SofiaSofia 畫邊一定是 xx 方向若干條,yy 方向若干條,我們直接枚舉 xx 方向的邊數,然後通過 nn 可以求出 yy 反向需要多少條邊,枚舉到最後取一個最小值即可。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;

int main() {
	int n; scanf("%d", &n);
	int ans = 1e9 + 10;
	for (int i = 1; i < maxn; ++ i) {
		if (n / i < i) break;
		if (n % i == 0) ans = min(i + n / i, ans);
		else ans = min(ans, i + n / i + 1);
	}
	printf("%d\n", ans);
	return 0;
}

C. Postcard(貪心 + 模擬)

  題意 :AndreyAndrey 收到了一張明信片,這張明信片是被加密過的,明信片的內容包含了小寫字母,?* 三種,其中 ? 表示在我們解密的時候我們可以刪除它前面的一個字符,或者將前面一個字符保留,* 表示我們可以刪除、保留前面一個字符,或者將前面一個字符複製若干份。現在給我們一個字符串和一個 nn 值,問我們是否能將這個字符串解密爲長度爲 nn 的字符串。
  思路 :我們以 * 作爲切入點,首先我們統計三種不同的內容的個數,如果沒有 * 就表示我們無法增加這個字符串的數量,那麼此時如果我們假設將所有 ? 之前的字母全部刪除剩餘一些字母,判斷 nn 是否大於這些字母個數,以及 nn 是否小於原本的字母個數,如果在說明可以得到一個解密後的字符串,否則輸出 Impossible,可以得到的字符串我們貪心的刪除就可以了。如果有 * 我們就需要判斷這個字符串是需要增加長度還是減少長度,如果是增加我們就遍歷字符串遇到第一個 * 之候我們就貪心的增加即可,如果是需要減少長度我們只需要遍歷字符串貪心的去刪除即可,如果二者都不是則輸出 Impossible

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
string str;

int main() {
	cin >> str;
	int n; cin >> n;
	int cnt1 = 0, cnt2 = 0, cnt3 = 0, len = str.length();
	for (int i = 0; i < len; ++ i) {
		if (str[i] == '?') ++ cnt1;
		else if (str[i] == '*') ++ cnt2;
		else ++ cnt3;
	}
	if (cnt2) {
		if (n >= cnt3 -(cnt2 + cnt1)) {
			if (n <= cnt3) {
				int xxx = cnt3 - n;
				int i = 0;
				string ans = "";
				while (i < len && xxx) {
					if (str[i + 1] == '?' || str[i + 1] == '*') {
						i += 2;
						-- xxx;
					} else {
						ans += str[i];
						++ i;
					}
				} 
				while (i < len) {
					if (str[i] != '*' && str[i] != '?') ans += str[i];
					++ i;
				}
				cout << ans << endl;
			} else {
				int xxx = n - cnt3;
				string ans = "";
				int i = 0;
				while (i < len) {
					if (str[i] == '*') break;
					if (str[i] <= 'z' && str[i] >= 'a') ans += str[i];
					++ i;
				}
				//cout << str[i - 1] << endl;
				for (int j = 0; j < xxx; ++ j) ans += str[i - 1];
				while (i < len) {
					if (str[i] <= 'z' && str[i] >= 'a') ans += str[i];
					++ i;
				}
				cout << ans << endl;
			}
		} else cout << "Impossible" << endl;
	} else {//無 * 
		if (cnt1) {
			if (n >= cnt3 - cnt1 && n <= cnt3) {
				int xxx = cnt3 - n;//需要刪除?
				int i = 0;
				string ans = "";
				while (i < len && xxx) {
					if (str[i + 1] == '?') {
						i += 2;
						-- xxx;
					} else {
						ans += str[i];
						++ i;
					}
				}
				while (i < len) {
					if (str[i] != '?') ans += str[i];
					++ i;
				}
				cout << ans << endl;
			} else cout << "Impossible" << endl;	
		} else {
			if (n == cnt3) cout << str << endl;
			else cout << "Impossible" << endl;
		} 
	}
	return 0;
}

D. Sum in the tree(樹上貪心)

  題意 :現在有一顆數,每一個結點有兩個值,一個是結點的權值,一個是根結點到當前結點的總權值(包含當前結點),現在給出一棵樹,給出根結點到當前結點的總權值,給出的總取值偶數層結點的總權值爲 -1,現在讓我們求每一個結點的權值,並且保證這顆樹的總權值最小。
  思路:一邊 dfs 一邊更新權值,在 dfs 的時候我們記錄當前點,當前點的父親結點以及到達當前點的權值(不包含當前結點) sumsum,首先如果當前點的權值不是 -1,如果當前點的 s[u]<sums[u] < sum 表示當前點的權值就可以更新,否則,表示這課樹不合法;如果當前點的權值是 -1,那麼我們就找到他的孩子結點中總權值最小的值,我們可以使用這個值去更新當前點的值,如果這個最小值小於當前的 sumsum 表明無法構成一顆樹, 否則我們就可以更新這個結點,這裏使用了貪心的思想,選擇最小的是保證到達這個最小的點的時候還是一顆合法的樹,使用最小的更新它的父親結點可以將權值最小化,因爲如果不是更新父親結點在往下走要更新兒子結點的時候,每一個兒子結點都需要加上相應的值,這個權值就會增加,這裏的大概意思就是如果在父親結點加一,那麼在兒子結點各自增加一,如果兒子結點有多個那麼總權值就會相應的增加。知道了這個貪心的策略以後既可以解題了。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e5 + 10;
int a[maxn], s[maxn];

vector<int>G[maxn];

bool dfs(int u, int fa, int sum) {
    if (s[u] != -1) {
        if (sum > s[u]) return false;
        a[u] = s[u] - sum;
        for (auto v : G[u]) {
            if (v == fa) continue;
            if (!dfs(v, u, sum + a[u])) return false;
        }
    } else {
        int Min = 1e9 + 10;
        for (auto v : G[u]) {
            if (v == fa) continue;
            Min = min(s[v], Min);
        }
        if (Min < sum) return false;
        if (Min != 1e9 + 10) a[u] = Min - sum;
        for (auto v : G[u]) {
            if (v == fa) continue;
            if (!dfs(v, u, sum + a[u])) return false;
        }
    }
    return true;
}

int main () {
    int n; scanf("%d", &n);
    for (int i = 2; i <= n; ++ i) {
        int x; scanf("%d", &x);
        G[x].push_back(i);
        G[i].push_back(x);
    }
    for (int i = 1; i <= n; ++ i) scanf("%d", &s[i]);
    bool flag = dfs(1, -1, 0);
    if (!flag) puts("-1");
    else {
        //for (int i = 1; i <= n; ++ i) cout << a[i] << " "; cout << endl;
        ll ans = 0;
        for (int i = 1; i <= n; ++ i) ans += (ll)a[i];
        printf("%lld\n", ans);
    }
    return 0;
}

Aggressive cows(最大化最小值)

題目鏈接 :http://poj.org/problem?id=2456
  題意 :現在有 nn 個位置,每一個值表示一個位置,我們需要把 cc 頭放在一些位置中,但是又要保證他們之前要有一定距離,現在問我們是否可以最大化這個距離。
  思路 :我們直接去二分這個距離,判斷在這個距離下是否能夠將所有的牛都可以安置好,如果可以就判斷更長的距離是否可以安置好,否則就判斷更短的距離是否可以安置好,最終我們就可以得到一個合適的最長的距離最爲答案輸出。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 10;
int x[maxn];
int n, c;

bool check(int dis) {
	int now = x[0];
	for (int i = 2; i <= c; ++ i) {
		int inx = lower_bound(x, x + n, now + dis) - x;
		if (inx == n) return false;
		now = x[inx];
	}
	return true;
}

int main () {
	scanf("%d%d", &n, &c);
	for (int i = 0; i < n; ++ i) scanf("%d", &x[i]);
	sort(x, x + n);
	int l = 0, r = 1e9, ans = 0;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) {
			l = mid + 1;
			ans = mid;
		} else r = mid - 1;
	}
	printf("%d\n", ans);
	return 0;
} 

String Game(最大化最小值)

題目鏈接 :http://codeforces.com/contest/779/problem/D
  題意 :現在有一個字符串,目前有一個可以順序刪除某一個位置的字符的序列,按照這個序列就可以刪除相應的字符,現在有兩個人,第一個人按照序列的順序去刪除字符,第二個人可以任意的順序去刪除字符,現在在給出一個字符串,問第二個人最晚在什麼時候選擇中斷第一個人的動作纔可以保證第二個人在刪除字符的時候可以得到第二個字符串。
  思路 :我們二分這個最晚的次數,判斷這個序列在這個次數內剩下的字符串中是否包含第二個字符串即可。需要注意的是這裏的子串不一定是連續的,所以只要判斷剩餘的字符串中是否順序的有第二個字符串中的字符即可。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e5 + 10;
int a[maxn];
bool vis[maxn];

string str, str1;
int len, len1;

bool check(int cnt) {
	for (int i = 0; i < len; ++ i) vis[i + 1] = false;
	for (int i = 0; i < cnt; ++ i) vis[a[i]] = true;
	int cnt1 = 0;
	for (int i = 0; i < len; ++ i) {
		if (!vis[i + 1] && str[i] == str1[cnt1]) ++ cnt1;
	}
	return cnt1 == len1;
}

int main () {
	cin >> str >> str1;
	len = str.length(), len1 = str1.length();
	for (int i = 0; i < len; ++ i) cin >> a[i];
	int l = 0, r = maxn, ans = 0;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) {
			l = mid + 1;
			ans = mid;
		} else r = mid - 1;
	}
	cout << ans << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章