书的复制

- 请奆佬们洁身自好,好好打代码从我做起 -

题目大意:

现在把m本有顺序的书分给k个人复制,每个人的速度都一样,

一本书只允许给一个人抄写,分给每一个人的书,必须是连续的,

现在请你设计一种方案,使得复制时间最短,

复制时间为抄写页数最多的人用去的时间。


Part1:二分答案

本题最优解

枚举每人抄的页数

最后得到每个人的分量都小于l

二分还是有不可代替的优势

毕竟可以将时间复杂度降至log级别

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int m, k, sum, l, r, a[505], s[505];
bool pd(int x) //判断与解的关系
{
    int sum = 0, ans = 1;
    for (int i = 1; i <= m; i++) 
	{
        if (sum + a[i] <= x)
        {
        	sum = sum + a[i];
		}     
        else 
		{
            sum = a[i];
            ans++;
        }
    }
    return ans <= k;
}
int main() 
{
    scanf("%d%d",&m,&k);
    for (int i = 1; i <= m; i++) 
	{
        scanf("%d",&a[i]);
        sum += a[i];
        if (a[i] >= l)
		 {
            l = a[i];//最少也要抄最大份的页数
        }
    }
    r = sum;
    while (l < r) {//二分答案
        int mid = (l + r) / 2;
        if (pd(mid))
        {
        	r = mid;
		}           
        else
        {
        	l = mid + 1;
		}         
    }
    sum = 0;
    for (int i = k, j = m; i >= 1; i--) //求过程
	{
        while (j >= 1 && sum + a[j] <= l) 
		{
            sum += a[j];
            j--;
        }
        s[i] = j + 1;
        sum = 0;
    }
    s[k + 1] = m + 1;//s[i]表示第i个人开始抄的书
    for (int i = 1; i <= k; i++) 
	{
        printf("%d %d\n", s[i], s[i + 1] - 1);
    }
    return 0;
}

很简单,也很有意思,不懂的建议多看看,多想想


Part2:动态规划

赵奆的,已授权

设f(i,j)表示把前i本书分成j份时最多要抄的

公式:

\[f(i,j) = min(max(f(k,j - 1), sum(k,i)) \]

sum为区间和

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n, k, a[600], f[600][600], s[600];
void dg(ll x, ll y) {//递归求过程
    if (x <= 0 || !y) {
        return;
    }
    if (y == 1) {
        cout << 1 << " " << x << endl;
        return;
    }
    ll t = x, o = a[x];
    while (t > 1 && o + s[t - 1] - s[t - 2] <= f[n][k] && t > 1) {
        o = o + s[t - 1] - s[t - 2];
        t--;
    }
    dg(t - 1, y - 1);
    cout << t << " " << x << endl;
}
int main() {
    ll i, j, l;
    cin >> n >> k;
    for (i = 1; i <= n; i++) {
        cin >> a[i];
        f[i][1] = s[i] = s[i - 1] + a[i];//前缀和,初始化
        //前i本分成一份就是1~i的和
    }
    for (j = 2; j <= k; j++) {//动态规划
        for (i = 1; i <= n; i++) {
            f[i][j] = 9e9;
            for (l = 1; l <= i; l++) {//枚举k(这里是l)
                f[i][j] = min(f[i][j], max(f[l][j - 1], s[i] - s[l]));
            }
        }
    }
    dg(n, k);
    return 0;
}

性能上看,没二分答案好


Part3:记忆化搜索

和动态规划都大同小异了

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
long long n, k, a[600], f[600][600], sum[600];
void dfs(long long x, long long y) {//求过程
    if (!x || !y) {
        return;
    }
    if (y == 1) {
        cout << 1 << " " << x << endl;
        return;
    }
    long long t = x, h = a[x];
    while (t > 1 && h + sum[t - 1] - sum[t - 2] <= f[n][k] && t > 1) {
        h += a[t - 1];
        t--;
    }
    dfs(t - 1, y - 1);
    cout << t << " " << x << endl;
}
long long dg(long long i, long long j) {//记忆化搜索
    if (j == 1)//分成一份,边界
        // //前i本分成一份就是1~i的和
        return sum[i];
    if (f[i][j])
        return f[i][j];
    f[i][j] = 1E9;
    for (int k = 1; k <= i; k++) {//枚举k
        f[i][j] = min(f[i][j], max(dg(k, j - 1), sum[i] - sum[k]));
    }
    return f[i][j];
}
int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];
    }
    dg(n, k);
    dfs(n, k);
    return 0;
}

这次打的仓促,见谅!

若有不懂,务必指出!

版权归某邓吖所有,禁止未经作者同意搬运!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值