SMU Summer 2024 div2 1st


提示:这里可以添加本文要记录的大概内容:

一、前言

暑假第一周训练,直接九九六了,俩个专题题单共四十题,每天一场个人赛俩到三个小时,还有对应的题单课程和题解,都在前面几篇博客里了反正。就不一一放链接了,然后酌情放几题没做出来的,着重总结一下算法学习和题单的题吧。


二、算法

1.二分算法

二分算法其实我写过好几次了,但每次写相对应的题都会遇到不会的,希望这次真的是最后一次写吧。

//二分模板
#include<bits/stdc++.h>

using namespace std;
#define int long long

bool check(int x) {
    //具体判断的条件
    return ;
    //判断是否符合
}

signed main() {
    int l,r;
    while (l < r) {
        int mid = (l+r+1)/2;
        if(check(mid))l = mid;
        else r = mid - 1;
        //int mid = (l+r)/2;
        //if(check(mid))r = mid;
        //else l = mid + 1;
    }//mid是否加1取决于输出l和r的判断
    //以非注释部分为例,l直接等于mid,如果不加1,直接取符合条件的偏小数,可能陷入死循环。
    cout << l << endl;
   //因为l是最后的符合条件的数字。
   return 0;
   //小tips,必须返回不然会boom,爆!
}

<1> ([CQOI2010])

扑克牌
a不是什么难题,就是一道典型题变式,拿出来看看。
题解:
你有n种牌,第i种牌的数目为ci。另外有一种特殊的牌:joker,它的数目是m。你可以用每种牌各一张来组成一套牌,也可以用一张joker和除了某一种牌以外的其他牌各一张组成1套牌。最多组成多少组牌。
x代表能否组成这么多牌,少的牌需要joker来代替。具体见代码。

代码:

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

long long n,m;
long long c[55];

bool solve(long long x) {
    long long ans = 0;
    for (int i = 0; i < n; i++) {
        if (c[i] < x)ans+=(x-c[i]);
    }
    if (ans <= x && ans <= m)return true;
    else return false;
}

int main() {
    cin >> n >> m;
    long long ma = 0;
    for (int i = 0; i < n; i++) {
        cin >> c[i];
        if (c[i] > ma)ma = c[i];
    }
    long long l = 0, r = 1e9;
    while (l < r) {
        long long mid = (l+r+1)/2;
        if (solve(mid)) {
            l = mid;}
        else r = mid-1;
    }
    cout << l;
    return 0;
}

<2> (USACO 2017 Dec P)

Greedy Gift Takers
缕了好久才缕清楚这道题,然后做不出来,看题解也可看了好久才看懂,啧。
题解:
n头奶牛,每只奶牛接受礼物后会插到c【i】只牛之前,计算有多少只牛永远接受不到礼物。
找到最后一只接收到礼物的牛,重点在check里面但我有点忘记了dbq。
代码:

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

int n;
const int N = 100005;
int c[N];
int b[N];

bool check(long long p) {
    for (int i = 0; i < p-1; i++) {
        b[i] = c[i];
    }//它前面所有的牛都要插队
    sort(b, b+p-1);
    //救命忘记为什么要排序了,好像是前面会反复循环?
    int num = n - p;
    for (int i = 0; i < p-1; i++) {
        if (b[i] <= num)num++;
        //哟,插不到我前面
        else return false;
    }
    return true;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> c[i];
    }
    long long x = 0, y = n+1;
    long long mid = 0;
    while (x < y) {
        mid = (x+y+1)/2;
        if (check(mid)) {
            x = mid;}
        else y = mid-1;
 //       cout << x << ' ' << y << endl;
    }
    cout << n - x << endl;
    //让我看看有多少牛就是拿不到礼物
}

<3> (Round2 C)

Minimum Width
当时一道有点印象的二分题,做出来还挺高兴的
题解:
n个长度为l【i】的单词,每个单词间隔一空,行首不用间隔,给定最多m行,求窗口最小宽度。
二分并判断即可。
代码:

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

int n,m;
long long L[200005];
long long sum;

bool solve(long long x) {
    long long t = 0;
    long long ls = 0;
    for (int i = 0; i < n; i++) {
        if (t == 0) ls++;
        t += L[i];
        if (t < x) {
            t++;
        }
        else if (t == x) {
            t = 0;
        }
        else if (t > x) {
            t = L[i]+1;
            //t直接定在下一个单词开头
            ls++;
        }
    }
    if (ls <= m)return true;
    else return false;
}

int main() {
    cin >> n >> m;
    sum = n;
    long long ma = 0;
    for (int i = 0; i < n; i++) {
        cin >> L[i];
        sum += L[i];
        if (L[i] > ma) ma = L[i];
    }
    long long l = ma,r = sum;
    while (l < r) {
        long long mid = (l+r)/2;
        if (solve(mid)) {
            r = mid;
        }
        else l = mid+1;
    }
    cout << r << endl;
    return 0;
}

2.三分算法

三分跟二分类似,取俩个点即可。
老规矩写个模板

#include<bits/stdc++.h>

using namespace std;
#define int long long

signed main() {
        while (r - l > 5) {
            int mid1 = l + (r-l)/3;
            int mid2 = r - (r-l)/3;
            if (p(mid1) < p(mid2)) {
                l = mid1;
            }
            else r = mid2;
        }
        int ans = 0;
        for (int i = l ; i <= r; i++) {
            ans = max(ans,p(i));
        }
        //这一段很有意思啊,因为直接大于1的话会超时,所以留下几个直接遍历
        cout << ans << endl;
    }
    return 0;
}
for (int i = 0; i <= 300; i++){
        int mid1 = l+(r-l)/3;
        int mid2 = r-(r-l)/3;
        ans = min(ans,check(mid1));
        ans = min(ans,check(mid2));
        if(check(mid1) > check(mid2))l = mid1;
        else r = mid2;
    }

<1> (热身1 B)

Linear Approximation
去偷俩题三分题先。
题解:
给出一个包含n个数字的数组a,求b为多少时伤心值最小。
三分思想很简单,l从-1e9,r从1e9,一直取俩点并舍去不合适区间,直到剩下一些些的时候直接便利即可,只有五十分,不知道错哪了,具体见注释。
数学思维因为取点最后一定是在俩点中间,所以取最中点与取中间任意一点相等,直接取存在的中点即可。
代码:

//已AC
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>

using namespace std;
#define int long long

int n;
int a[200005];

int check(int x) {
    int res = 0;
    for(int i = 1; i <= n; i++) {
        res += abs(a[i]-x);
    }
    return res;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[i] = a[i] - i;
    }sort(a+1,a+n+1);
    int l = -1e12, r = 1e12+10;
    int ans = 4e18;
    for (int i = 0; i <= 300; i++){
        int mid1 = l+(r-l)/3;
        int mid2 = r-(r-l)/3;
        ans = min(ans,check(mid1));
        ans = min(ans,check(mid2));
        if(check(mid1) > check(mid2))l = mid1;
        else r = mid2;
        //如果写l=mid1-1,r=mid2+1,就是14/15,因为r-l=3,l=2,可能一直错过中间点
        //在这中间反复横跳,啊举了例子但是忘记了
    }
//    for (int i = l; i <= r; i++) {
//        ans = min(ans,check(i));
//    }//顺手再检验一下
    cout << ans << endl;
    //不理解十足的不理解,让我再学学再来改
    //我来了!我懂了!我真的懂了!
    return 0;
}

<2> (热身1 D)

Equal Cut
题解:
给定含n个数字的数组a,切三刀变成三个数组的和分别是p,q,r,s,求最大值与最小值差值的最小值。
先中间切一刀,开始遍历这个中间数,然后左右分别切一刀,希望切下来的俩块使差值尽可能小。然后每个中间数的最小值比较即可。用了一些前缀和思想,方便运算。
代码:

#include<iostream>
#include<algorithm>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>

using namespace std;
#define int long long

int n;
int a[200008];
int qz[200008];

bool check(int x,int y, int aaa) {
    if(abs((x+a[aaa]) - (y-a[aaa])) < abs(x - y)) return true;
    else return false;
}//判断是否要加上aaa未知的数字a

signed main() {
    cin >> n;
    a[0] = 0;
    qz[0] = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        qz[i] = qz[i-1] + a[i];
    }

    int b = 1,e = 3;
    //b,e是左右俩刀
    int ans = 1e9+5;

    for (int i = 2; i <= n-2; i++) {
    //i是中点
        int p = qz[b];
        int q = qz[i] - qz[b];
        int r = qz[e] - qz[i];
        int s = qz[n] - qz[e];
        while (check(p,q,b+1) && b+1 < i) {
            b++;
            p = qz[b];
            q = qz[i] - qz[b];
        }//一直加到俩边尽可能接近
        while (check(r,s,e+1) && e+1 < n) {
            e++;
            r = qz[e] - qz[i];
            s = qz[n] - qz[e];
        }
        
        int test[10];
        test[0]=p;test[1]=q;
        test[2]=r;test[3]=s;
        sort(test,test+4);
        ans = min(ans,test[3]-test[0]);
        //每次遍历的最小值都要相比
    }

    cout << ans << endl;
    return 0;
}

三、总结

还有一个题单,写的题不太多,下次再改一下周报吧,就先写到这了。每日总结基本都在每日题解里了。

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值