第十一次周赛题解

前言:

百度之星选拔赛。

题目难度中规中矩。

前5道没什么太大难度,代码量很小,主要看你平时练习状态,摸鱼的一下就看出来了。

大一同学预计大部分出4、5题,少部分能出6题,大二同学预计6~7题,可能有AK。

题目详情:

题号

类型

思维

代码

A

签到

0

1

B

贪心

0

2

C

贪心STL/双指针

0

2

D

递推/简单DP

1

1

E

数学/推式子

2

1

F

二分

2

3

G

状压DP/二分图

3

3/2

H

二分图/最大流

3

2/3

问题 A: 来点鸡肉堡 

签到题,注意给出的eq?n是总人数即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, mod = 1e9 + 7;
 
void solve()
{
    int n, x, a, b;
    cin >> n >> x >> a >> b;
    cout << x * b + (n - x) * a << "\n";
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
     
    int t;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

问题 B: 数组询问 

很明显的贪心

1.循环一遍,sum记录非0的情况下的和,cnt统计0的数量。

2.最小值就是0的数量 * 区间左值(cnt*l),反之最大值就是0的数量*区间右值(cnt*r)。

3.答案就是最小:sum+cnt*l,最大:sum+cnt*r;

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, mod = 1e9 + 7;
 
void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    int sum = 0, cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        sum += a[i];
        if (!a[i])
        cnt++;
    }
    while (m--)
    {
        int l, r;
        cin >> l >> r;
        cout << sum + cnt * l << " " << sum + cnt * r << "\n";
    }
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
     
    int t;
    t = 1;
    while (t--)
    solve();
    return 0;
}

问题 C: 选数乘积 

贪心,数据保证eq?a_%7Bi%7D是正整数,尽可能每次乘最大的数。

但数据范围到1e9,开数组统计eq?a_%7Bi%7D有没有被删显然是做不到的。

可以考虑用set/map统计,

或者使用双指针判断a[i]和a[i+1]是否相同,根据你的排序,特判首部或尾部即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, mod = 1e9 + 7;

void solve()
{
    int x, y;
    cin >> x >> y;
    int n;
    cin >> n;
    vector<int> a;
    set<int> q;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        if (q.count(x))
            continue;
        a.push_back(x);
        q.insert(x);
    }
    sort(a.begin(), a.end(), greater<int>());
    if (x >= y)
    {
        cout << 0 << "\n";
        return;
    }
    for (int i = 0; i < a.size(); i++)
    {
        x *= a[i];
        if (x >= y)
        {
            cout << i + 1 << "\n";
            return;
        }
    }
    cout << -1 << "\n";
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    int t;
    t = 1;
    while (t--)
        solve();
    return 0;
}

 问题 D: 上台阶

一眼下去很典的递推。斐波那契数列eq?f%5Bi%5D%3Df%5Bi-1%5D&plus;f%5Bi-2%5D

但考虑到有台阶不能走,并且一次只能上1或2层台阶。

所以一旦有两个连续的台阶不能走,那么答案为0。

eq?f%5Bi-1%5D无法走,则eq?f%5Bi%5D无法通过eq?f%5Bi-1%5D到达,无需添加eq?f%5Bi-1%5D的方案数。

对于eq?f%5Bi-2%5D同理。

不要忘记取模!

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, mod = 1e9 + 7;
int f[N];//表示从第0层走到第i层的方案数
bool st[N];//判断第i个台阶能不能走,0表示能走,1表示不能走。
void solve()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x;
        cin >> x;
        st[x] = 1;
    }
    f[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        if (!st[i] && !st[i - 1])//判断第i个台阶和第i-1个台阶是否可以走
        f[i] += f[i - 1];
        if (i > 1 && !st[i] && !st[i - 2])
        f[i] += f[i - 2];
        f[i] %= mod;//别忘记取模
    }
    cout << f[n];
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
     
    int t;
    t = 1;
    while (t--)
    solve();
    return 0;
}

 问题 E: 简单数学问题

一道CCPC的原,很简单的推式子,没写出来的拿演草纸推一下。

31c1a3b7a17d47c99cb50b9025feca94.png

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define int long long
const int N = 1e5 + 10, mod = 1e9 + 7;
 
void solve()
{
    int x, y;
    cin >> x >> y;
    //lcm(a,b)=a*b/gcd(a,b),不知道的可以学习基础数论
    cout << 1 << " " << (x * y / __gcd(x, y)) / __gcd(x, y) << "\n";
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

问题 F: 整数反应 

二分+贪心,2024江苏CCPC,

为了减少查询时间,所以要保证每次放入数据后,序列是有序的,并且数据可以重复,考虑使用multiset模拟插入删除的过程。

multiset自带lower_bound二分查找函数。

859e751b6e6a45a290f599e7f1f820bf.png

fb101467cd1a410db5b630ba5b51bec8.png

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, mod = 1e9 + 7;
 
void solve()
{
    int n;
    cin >> n;
    vector<int> b(n + 1), a(n + 1);
    for (int i = 1; i <= n; i++)cin >> a[i];
    for (int i = 1; i <= n; i++)cin >> b[i];
    int l = 1, r = 1e9;
    auto check = [&](int mid) -> bool
    {
        multiset<int> q1, q2;
        for (int i = 1; i <= n; i++)
        {
            if (b[i] == 1)
            {
                if (q2.empty())
                {
                    q1.insert(a[i]);
                }
                else
                {
                    auto pos = q2.lower_bound(mid - a[i]);
                    if (pos == q2.end())
                    {
                        return 0;
                    }
                    else
                    {
                        q2.erase(pos);
                    }
                }
            }
            else
            {
                if (q1.empty())
                {
                    q2.insert(a[i]);
                }
                else
                {
                    auto pos = q1.lower_bound(mid - a[i]);
                    if (pos == q1.end())
                    {
                        return 0;
                    }
                    else
                    {
                        q1.erase(pos);
                    }
                }
            }
        }
         
        return 1;
    };
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid))
        l = mid;
        else
        r = mid - 1;
    }
    cout << l << "\n";
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
     
    int t;
    t = 1;
    while (t--)
    solve();
    return 0;
}

问题 G/F: 切蛋糕(easy/hard)

本质上是一道二分图最大匹配的问题。

二分图板子题,难在你能看出他是个二分图。

不会二分图的,可以去bilibili或者acwing的算法基础课进行学习。


G的数据范围比较小,n只有11,所以可以使用状态压缩DP求解。

与蒙德里安的梦想类似,时间复杂度eq?2%5E%7B%5E%7Bn%7D%7D*2%5E%7B%5E%7Bn%7D%7D*11约为4e7。


下面给出二分图做法:

考虑一个格子eq?%28i%2Cj%29

eq?i&plus;j为偶数:不妨记这样的格子为白格子.
eq?i&plus;j为奇数:不妨记这样的格子为黑格子.

c25d4df827c147bbb4677efd2a7650c8.png

将所有的偶数染成白色,所有奇数染成黑色(进行二染色)。

最后我们可以发现,每一个方块的四周,都不会有和它一样颜色的方块,也就是一块蛋糕不可能覆盖相同颜色的两个点(同一集合中不会有边)。

那我们将值为偶数的方块放到一个集合中,值为奇数的方块放到一个集合中,构造一个二分图,把题目中的蛋糕看作连接两个方块的一条边,切最多块蛋糕就可以看作找到最多的边。

最多O(n^2)个点,O(n^2)条边,所以时间复杂度O(n^4)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const int N = 110, mod = 1e9 + 7;
int n, t;
bool st[N][N], g[N][N];
pii match[N][N];
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
bool find(int xx, int yy)
{
    for (int i = 0; i < 4; i++)
    {
        int x = dx[i] + xx;
        int y = dy[i] + yy;
        if (x < 1 || x > n || y < 1 || y > n || g[x][y])
        continue;
        if (!st[x][y])
        {
            st[x][y] = 1;
            auto [lx, ly] = match[x][y];
            if (lx == 0 || find(lx, ly))
            {
                match[x][y] = {xx, yy};
                return 1;
            }
        }
    }
    return 0;
}
void solve()
{
     
    cin >> n >> t;
    int ans = 0;
    for (int i = 1; i <= t; i++)
    {
        int x, y;
        cin >> x >> y;
        g[x][y] = 1;
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if ((i + j) % 2 == 0 && !g[i][j])
            {
                memset(st, 0, sizeof st);
                if (find(i, j))
                ans++;
            }
        }
    }
    cout << ans << "\n";
}
 
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
     
    int t;
    t = 1;
    while (t--)
    solve();
    return 0;
}

本题还有最大流做法:

通过横纵坐标之和的奇偶性,将整张图划分为二分图,将合法的奇数点与 源点 S 相连, 将合法的偶数点与汇点 T 相连,由于每个点只能用一次,所以每条边的容量为 1 , 然后将奇数点和其相邻的合法偶数点相连,跑一边最大流算法就行了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值