贪心题
CF 上的第三题有一些是贪心,我经常滑铁卢,特此总结。
遇到贪心题目不要着急,猜猜结论,尝试证明一下(最常用邻项交换)。
CF1608C
非常牛逼的贪心题目。
分析:我们的贪心策略是先做一些不降智的比赛,在一个时间节点之后每场比赛都做,直到 \(q=0\),此时刚好结束。考虑证明这个贪心的正确性。
- 在确定最后达到的 \(q\) 的情况下,尽可能把需要降智的比赛放到后面。
理由:如果放到前面,可能会多出一些不降的,不优。 - 在所有的最后达到的 \(q\) 中,\(q=0\) 最优。
理由:假设取 \(q=0\)。
我们尝试让 \(q=1\),证明这个方案不优。
考虑倒过来思考这个过程,即:一开始拥有 \(q\) 的智力。
- 如果 \(q \ge a_i, \operatorname{do} a_i\)
- 如果 \(q < a[i]\),如果 \(q < q_{初始},\operatorname{do} a_i,q++\) 否则 \(\operatorname{skip} a_i\)
我们取 \(1\),一开始一些降智比赛大家都一样,到了第一个 \(a[i]=1\) 的比赛的时候,\(1\) 不降智参赛,\(0\) 降智参赛,并且升到 \(1\),和 \(1\) 在后面的表现一样。
如果没有这种比赛,那 \(0\) 比 \(1\) 多打一场降智比赛。比较优。
同理 \(1\) 比 \(2\) 优。
以此类推!
然后随便贪。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
vector<int> a;
vector<bool> join;
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
int t; cin >> t;
while(t--) {
int n, q; cin >> n >> q;
cl(a, n + 10); cl(join, n + 10);
f(i, 1, n) cin >> a[i];
int nq = 0;
for(int i = n; i >= 1; i--) {
if(nq >= a[i]) {
join[i] = 1;
}
else if(nq < a[i] && nq < q) {
nq++; join[i] = 1;
}
else join[i] = 0;
}
f(i, 1, n) cout << join[i];
cout << endl;
}
return 0;
}
CF1709C
题意:给定一个匹配括号序列,将部分位置变成问号,问是否有唯一的方案将所有问号变成左括号或右括号,使得该括号序列依然为匹配的。
分析:
首先我们考虑构造一组方案。先算出括号中左括号和右括号的位置。当左括号没用完的时候我们优先使用左括号。否则使用右括号。
为什么这么做呢?证明:因为题目保证一定存在一组合法的括号序列,故一定有一种合法的序列。假设这种方案(记为 \(S\))不成立,那么一定存在另一种方案(记为 \(T\))成立。并且这种方案一定是 \(S\) 中经历若干次左右括号交换而来的。考虑在 \(S\) 的某个位置 \(x\) 出现不成立的情况,说明 \(1 \sim x\) 中右括号数量大于左括号数量。考虑被交换的位置 \(y,z\),满足 \(S_y=\mathtt{'('},S_z=\mathtt{')'}\),则 \(y<z\) 一定成立。
- 如果 \(x<y<z\),那么交换之后,\(x\) 左边右括号和左括号数量不变,依然会在 \(x\) 处不成立。
- 如果 \(y<x<z\),那么交换之后,\(x\) 左边会多出一个右括号少掉一个左括号,更不会成立。
- 如果 \(y<z<x\),那么交换之后,\(x\) 左边右括号和左括号数量不变,依然会在 \(x\) 处不成立。
与假设矛盾,原命题成立。
然后我们考虑是否能构造出其他方案合法。考虑这个方案:将 \(S\) 中最后一个左括号和第一个右括号交换,依然类似的思路证明该方案次优。判断这种方案是否可行,如果可行那么 \(\mathtt{NO}\),否则 \(\mathtt{YES}\)。(如果不存在这种方案,也就是问号中没填左/右括号,一定 \(\mathtt{YES}\))。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int l, r, q, nl, nr;
stack<int> lft; queue<int> rgt;
bool check(string s){
int cnt=0;
f(i,0,(int)s.size()-1){if(s[i]=='(')cnt++;else cnt--;if(cnt<0)return 0;}
return 1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
int t; cin >> t;
while(t--) {
while(!lft.empty())lft.pop();while(!rgt.empty())rgt.pop();
string s; cin >> s; int n = s.size();l=r=0;
f(i, 0, (int)s.size() - 1) {
if(s[i]=='(')l++; else if(s[i]==')')r++;
}
nl=(n/2)-l, nr=(n/2)-r;
f(i, 0, (int)s.size() - 1) {
if(s[i]=='?'){
if(nl) {lft.push(i);s[i]='(';nl--;}
else {rgt.push(i);s[i]=')';nr--;}
}
}
if(lft.empty()||rgt.empty()) {cout<<"YES\n";continue;}
int x=lft.top(), y=rgt.front();
swap(s[x],s[y]); if(check(s)){cout<<"NO\n";} else {cout<<"YES\n";}
}
return 0;
}