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

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

A. Snowball(模拟)

  题意 :现在有一个高为 (h),重量为 (w) 的一个雪球,每一秒钟都会发生一些事情,雪球的重量会增加 (h),如果当前高度到达了某一个石头的高度那么它就会撞击这个石头,此时雪球的重量相应的减少一定的重量,减少的重量就是这个石头的重量,然后这个雪球的高度下降 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
", w);
	return 0;
}

B. Squares and Segments(枚举)

  题意 :(Sofia) 想要画一些正方形,但是她需要画出来一些边进行参考, 剩下的需要的边是从这些边平移出来的。现在给出 (Sofia) 想要画出来的正方形的个数,问刚开始 (Sofia) 最少需要画出多少条边?
  思路 :(Sofia) 画边一定是 (x) 方向若干条,(y) 方向若干条,我们直接枚举 (x) 方向的边数,然后通过 (n) 可以求出 (y) 反向需要多少条边,枚举到最后取一个最小值即可。

#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
", ans);
	return 0;
}

C. Postcard(贪心 + 模拟)

  题意 :(Andrey) 收到了一张明信片,这张明信片是被加密过的,明信片的内容包含了小写字母,?* 三种,其中 ? 表示在我们解密的时候我们可以删除它前面的一个字符,或者将前面一个字符保留,* 表示我们可以删除、保留前面一个字符,或者将前面一个字符复制若干份。现在给我们一个字符串和一个 (n) 值,问我们是否能将这个字符串解密为长度为 (n) 的字符串。
  思路 :我们以 * 作为切入点,首先我们统计三种不同的内容的个数,如果没有 * 就表示我们无法增加这个字符串的数量,那么此时如果我们假设将所有 ? 之前的字母全部删除剩余一些字母,判断 (n) 是否大于这些字母个数,以及 (n) 是否小于原本的字母个数,如果在说明可以得到一个解密后的字符串,否则输出 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 的时候我们记录当前点,当前点的父亲结点以及到达当前点的权值(不包含当前结点) (sum),首先如果当前点的权值不是 -1,如果当前点的 (s[u] < sum) 表示当前点的权值就可以更新,否则,表示这课树不合法;如果当前点的权值是 -1,那么我们就找到他的孩子结点中总权值最小的值,我们可以使用这个值去更新当前点的值,如果这个最小值小于当前的 (sum) 表明无法构成一颗树, 否则我们就可以更新这个结点,这里使用了贪心的思想,选择最小的是保证到达这个最小的点的时候还是一颗合法的树,使用最小的更新它的父亲结点可以将权值最小化,因为如果不是更新父亲结点在往下走要更新儿子结点的时候,每一个儿子结点都需要加上相应的值,这个权值就会增加,这里的大概意思就是如果在父亲结点加一,那么在儿子结点各自增加一,如果儿子结点有多个那么总权值就会相应的增加。知道了这个贪心的策略以后既可以解题了。

#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
", ans);
    }
    return 0;
}

Aggressive cows(最大化最小值)

题目链接 :http://poj.org/problem?id=2456
  题意 :现在有 (n) 个位置,每一个值表示一个位置,我们需要把 (c) 头放在一些位置中,但是又要保证他们之前要有一定距离,现在问我们是否可以最大化这个距离。
  思路 :我们直接去二分这个距离,判断在这个距离下是否能够将所有的牛都可以安置好,如果可以就判断更长的距离是否可以安置好,否则就判断更短的距离是否可以安置好,最终我们就可以得到一个合适的最长的距离最为答案输出。

#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
", 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;
}
原文地址:https://www.cnblogs.com/zut-syp/p/12778776.html