寒假思维训练计划Day6

宣传一下我对构造题总结的方法论:

 1、前后缀贪心,比如说观察前后缀的sum,去看以后怎么考虑最好。Problem - 1903C - Codeforces

2、双指针贪心法,考虑两端相消或者相互作用,还有就是考虑左右边界。   Problem - 1891C - Codeforces

Problem - 1907D - Codeforces

3、转换观察法,有些关系可以抽象成图,观察图的某些性质去总结规律。也可以抽象成一个集合,两个集合相等可以说明有解可构造。Problem - 1891C - Codeforces

4、打表找规律,一般没什么规律可循即可打表找规律,一般和数论有关的很喜欢考,acm也喜欢考,属于人类智慧题。Problem - 1916D - Codeforces

5、公式推导演算,常见的分为公式的等价变形、公式的化简(这个常考,一般需要先证明某些性质,可以直接抵消,一般如果原公式处理起来很复杂时就可以考虑)。Problem - 1889B - Codeforces

6、考虑奇偶数去简化问题或者分类问题,从其中的一些运算性质入手,因为奇数偶数的加减以及%运算(这个结论很重要)的结果的奇偶性是固定的,Problem - 1898C - Codeforces

7、根据性质构造模型,看看能不能分成几个块,几个不同的集合,再选择算法去解决。Problem - 1873G - Codeforces

8、考虑从小到大处理,或者是从大到小处理,有时候先处理小的对大的不会有影响,或者反过来,这样的处理顺序是最完美的。Problem - 1904D2 - Codeforces

9、边界贪心法,一般要在问题的最边界处考虑,有时候这样做结果是最优的,或者考虑边界上的影响,假如让影响最小,就使得影响<= 固定值 。 ​​​​​​Problem - E - Codeforces and Problem - 1903C - Codeforces


Day6(牛客练习赛):

1、A-抽奖黑幕_牛客练习赛120 (nowcoder.com)
序列 a 的前 n−1 个元素已经确定,请确定最小的正整数 a[n]​,使得序列 a 去除一个最小值和最大值之后的总和最大。数据范围是>=1

显然,最大值最小值相等时直接输出1,否则直接输出max

#include  
#define int long long 
using namespace std; 
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n; 
int a[N]; 
void solve() {
    cin >> n;
    int mx = -inf, mn = inf, s = 0;
    for(int i = 1; i <= n - 1; i ++ ) 
        cin >> a[i], mn = min(mn, a[i]), mx = max(mx, a[i]);
    if(mn==mx) {
        cout<<1<> T;
    while(T --) solve();
    return 0; 
}

2、B-生成函数_牛客练习赛120 (nowcoder.com)
题意: 有三种数量无限的砝码和一个天平,天平的一端有一个质量为 m的物品,问能否通过放置砝码使得天平平衡, 输入a,b,c,m

题解:这里我推导一下:

裴属定理:a x + by = gcd(a, b) * k

xa + yb + zc = gcd(a, b) * t + zc = gcd(gcd(a,b), c) * u = gcd(a, b, c) * u = m

代码:

#include 
#define int long long 
using namespace std; 
const int N = 2e5 + 10; 
int a, b, c, m;

int exgcd(int &x, int &y, int a, int b) {
    if(!b) {
        x = 1, y = 0; 
        return a; 
    }    
    int d = exgcd(y, x, b, a % b); 
    y -= x * (a / b); 
    return d; 
}

void solve() {
    cin >> a >> b >> c >> m;  
    int ans = __gcd(__gcd(a, b), c);
    if(m % ans) puts("NO"); 
    else puts("YES");
    
}
 
signed main() {
    int ts; 
    cin >> ts; 
    while(ts --) solve(); 
    
    return 0; 
}




3、C-选择交换_牛客练习赛120 (nowcoder.com)

寒假思维训练计划Day6_第1张图片

贪心思路:

排完序以后一定是最优解,假设排完序以后的状态:a\left [ 1 \right ],a\left [ 2 \right ],..., a\left [ n \right ]

假设a[1] + a[i],a[i] != a[n],那么a[2] + a[i + 1] >= a[1] + a[i],为什么是选a[2]呢,因为往后的大的应该尽量安排到小的a[x]上面,所以这样是会让结果偏大的。

代码:

#include  
#define ff first 
#define ss second 
using namespace std; 
using PII = pair;
const int N = 2e5 + 10; 
int n, m, b[N]; 
PII a[N]; 

void solve() {
    cin >> n;
    
    for(int i = 1; i <= n; i ++ ) cin >> a[i].ff, a[i].ss = i; 
    
    mapm1;
    mapm2;
    for(int i = 1; i <= n; i ++ ) {
        m1[a[i]] = i; 
        m2[i] = a[i]; 
    } 
    sort(a + 1, a + 1 + n); 
    int l = 1, r = n; 
    int x = a[l].ff + a[r].ff; 
    
    bool flag = 1; 
    while(l < r) {
        if(a[l].ff + a[r].ff != x) {
            flag = 0; 
            break; 
        }
        l ++, r --; 
    }
    if(n % 2 && 2 * a[n / 2 + 1].ff != x) flag = 0;
    if(!flag) {
        cout << "NO" << endl; 
        return; 
    }
    cout << "YES" << endl; 
    vector rs;
    for(int i = 1; i <= n; i ++ ) {
        if(m2[i] == a[i]) continue;
        PII t = a[i], t_ = m2[i];  
        int pos = m1[t]; 
        m1[t_] = pos, m1[t] = i;
        m2[pos] = t_, m2[i] = t; 
        rs.push_back({i,pos});
    }
    
    cout << rs.size() << endl; 
    for(auto t : rs) 
        cout << t.ff << ' ' << t.ss << endl;
    
}
int main() {
    int T;
    cin >> T; 
    while(T --) solve(); 
    
    return 0; 
}

  

你可能感兴趣的:(算法)