贪心题

CF 上的第三题有一些是贪心,我经常滑铁卢,特此总结。
遇到贪心题目不要着急,猜猜结论,尝试证明一下(最常用邻项交换)。

CF1608C

非常牛逼的贪心题目。
分析:我们的贪心策略是先做一些不降智的比赛,在一个时间节点之后每场比赛都做,直到 \(q=0\),此时刚好结束。考虑证明这个贪心的正确性。

  1. 在确定最后达到的 \(q\) 的情况下,尽可能把需要降智的比赛放到后面。
    理由:如果放到前面,可能会多出一些不降的,不优。
  2. 在所有的最后达到的 \(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\) 一定成立。

  1. 如果 \(x<y<z\),那么交换之后,\(x\) 左边右括号和左括号数量不变,依然会在 \(x\) 处不成立。
  2. 如果 \(y<x<z\),那么交换之后,\(x\) 左边会多出一个右括号少掉一个左括号,更不会成立。
  3. 如果 \(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;
}
posted @ 2022-07-22 10:09  OIer某罗  阅读(35)  评论(0编辑  收藏  举报