标签 洛谷 下的文章

url: https://www.luogu.com.cn/problem/P1281

tag:
二分,贪心

思路:
求可以满足条件的最小值,所以可以想到用二分来做。二分的是复制时间,所以范围是在0到全部的页数。二分的思路是利用这个复制时间来求出一个需要的人数,将这个人数和题目所给的认识做一个比较,因为要满足条件,所以人数需要小于等于题目给的人数。可以知道如果复制时间越大,表示一个人可以抄的书越多,需要的人数越少,所以人数和复制时间是单调递减的关系。利用这个单调递减的关系,可以二分出一个复制时间,使得这个复制时间是当所需人数小于等于所需人数时,复制时间最小。然后就可以通过这个复制时间来求出每一个人所抄的书的起始范围。因为要求是让前面的人尽可能的少抄,所以就需要让后面的人多抄,因此再输出答案的时候用到了贪心的思想,从后往前遍历书,使得每次每个人都尽可能抄多一点的书,通过这种遍历方式实现题目要求的让前面的人尽可能少抄。在二分的时候也有用到这种遍历方式,但是后面写题解的时候觉得或许二分的时候不需要,因为只是求一个人数,没有前后之分。

代码:

#include <iostream>
using namespace std;
int m, k;
const int N = 550;
int a[N];
int x[N], y[N];
bool check(int x)
{
    int now = 1;
    int cnt = 0;
    for (int i = m - 1; i >= 0; i --)
    {
        if (cnt + a[i] > x)
        {
            cnt = 0;
            now ++;
        }
        cnt += a[i];
    }
    return now <= k;
}
int main()
{
    cin >> m >> k;
    int kk = k;
    int l = 0, r = 0;
    for (int i = 0; i < m; i ++)
    {
        cin >> a[i];
        r += a[i];
    }
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    int t = 0;
    y[k] = m; x[1] = 1;
    for (int i = m - 1; i >= 0; i --)
    {
        if (t + a[i] > l)
        {
            t = 0;
            x[k] = i + 2;
            y[--k] = i + 1;
        }
        t += a[i];
    }
    for (int i = 1; i <= kk; i ++)
    {
        cout << x[i] << ' ' << y[i] << '\n';
    }
    return 0;
}

url: https://www.luogu.com.cn/problem/P1827

tag:
递归, 二叉树

代码:

#include <iostream>
#include <string>
using namespace std;

string inorder, preorder;

void buildPostorder(int inStart, int inEnd, int preStart, int preEnd) {
    // 若范围无效,则返回
    if (inStart > inEnd || preStart > preEnd) {
        return;
    }

    // 前序遍历的第一个元素即为根节点
    char root = preorder[preStart];

    // 在中序遍历中找到根节点的位置
    int rootIndex = -1;
    for (int i = inStart; i <= inEnd; ++i) {
        if (inorder[i] == root) {
            rootIndex = i;
            break;
        }
    }

    // 根节点左边的节点个数
    int leftTreeSize = rootIndex - inStart;

    // 递归处理左子树
    buildPostorder(inStart, rootIndex - 1,
                   preStart + 1, preStart + leftTreeSize);

    // 递归处理右子树
    buildPostorder(rootIndex + 1, inEnd,
                   preStart + leftTreeSize + 1, preEnd);

    // 最后输出根节点,实现后序遍历顺序:左子树 -> 右子树 -> 根
    cout << root;
}

int main() {
    // 输入中序遍历和前序遍历字符串
    cin >> inorder >> preorder;

    int n = inorder.size();
    buildPostorder(0, n - 1, 0, n - 1);

    return 0;
}

url: https://www.luogu.com.cn/problem/P2004

tag:
前缀和

思路:
先求前缀和,然后遍历右下端点,求出每个对应的子矩阵的总和,判断是否大于res,如果大的话就更新res和坐标x,y。最后输出x和y即可。

ps:这道题很简单,本来不应该传博客的,但是觉得自己写的好优美,忍不住传一下,以后偶尔可以翻出来看看。真的好优美感觉。

代码:

#include <iostream>
using namespace std;
const int N = 1010;
long long p[N][N];
int n, m , c;
int main()
{
    cin >> n >> m >> c;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
        {
            cin >> p[i][j];
            p[i][j] += p[i - 1][j] + p[i][j - 1] - p[i - 1][j - 1];
        }
    long long res = 0, x, y;
    for (int i = c; i <= n; i ++)
        for (int j = c; j <= m; j ++)
        {
            long long tmp = p[i][j] - p[i - c][j] - p[i][j - c] + p[i - c][j - c];
            if (tmp > res)
            {
                res = tmp;
                x = i - c + 1;
                y = j - c + 1;
            }
        }
    cout << x << ' ' << y;
    return 0;
}

url: https://www.luogu.com.cn/problem/P4343

tag:
二分,模拟

思路:
思路比较简单,根据题目可以知道当n越小时切出来的题目数量越多,根据这个来二分。分别二分出一个最小值和一个最大值。这道题是细节比较 恶心(bushi 多,需要注意的点比较多。第一个是二分的范围,题目只有一个xi的范围是1e-9到1e9,经测试,r开到1e9范围比较小,看了题解之后发现需要开到1e18.然后这里就有一个问题,因为超过了1e9所以变量不能放到main()函数中,只能放到外面当全局变量。然后如果是mid要用long long类型,记得check函数的函数签名中变量的类型也要是long long。第二个细节是l要从1开始,不能从0开始。第三个细节是,题目没有保证说如果其中一个答案比如最小值存在,另外一个答案就会存在,所以对于最小值和最大值都要进行验证。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
long long n, k, l ,r;
long long p[N];

long long check (long long x)
{
    long long sum = 0, cnt = 0;
    for (int i = 0; i < n; i ++)
    {
        if (p[i] > 0) sum += p[i];
        else sum = max((long long)0, sum + p[i]);
        if (sum >= x)
        {
            sum = 0;
            cnt ++;
        }
    }
    return cnt;
}
int main()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 0; i < n; i ++) scanf("%lld", &p[i]);
    l = 1, r = 1e18;

    while (l < r)
    {
        long long mid = (l + r) >> 1;
        if (check(mid) <= k) r = mid;
        else l = mid + 1;
    }
    long long tmp;
    if (check(l) != k)
    {
        cout << -1 ;
        return 0;
    }
    else tmp = l;
    l = 1, r = 1e18;
    while (l < r)
    {
        long long mid = (l + r + 1) >> 1;
        if (check(mid) >= k) l = mid;
        else r = mid - 1;
    }
    if (check(l) != k)
    {
        cout << -1 ;
        return 0;
    }
    else cout << tmp << ' ' << l;
    return 0;
}