P1800 software [dp+二分答案]

9 篇文章 0 订阅

s o f t w a r e software software


D e s c r i p t i o n \mathcal{Description} Description

一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。写一个程序,求出公司最早能在什么时候交付软件

n , m &lt; = 100 n,m&lt;=100 n,m<=100


S o l u t i o n \mathcal{Solution} Solution

最 初 想 法 最初想法

开始想到设 F [ i , j , k ] F[i,j,k] F[i,j,k] 表示前 i i i 个技术人员, 生产完成第一个软件 j j j 个模块和第二个软件的 k k k 个模块 所需要的 最小时间 .
但是发现加上 状态转移后 时间复杂度高达 O ( N 5 ) O(N^5) O(N5), 于是放弃这个思路.
好蠢


正 解 部 分 正解部分

如果能在 x x x 时间内完成, 则必定也能在 y &gt; x y&gt;x y>x 时间内完成, 答案具有 单调性.
于是考虑 二分 m i d mid mid, 检查其是否合法.

将员工看成 N N N 种物品, 软件一模块数量 M M M 看成背包容量,
相当于 N N N 种物品去装满这个大小为 M M M 的背包, 且背包为 完全背包.


实 现 部 分 实现部分

则设 F [ i , j ] F[i,j] F[i,j] 表示前 i i i 个员工, 完成 j j j 个软件一模块 , 最多能完成多少软件二模块,

先二分出 m i d = L + R &gt; &gt; 1 mid=L+R &gt;&gt; 1 mid=L+R>>1,
L = 0 , R = 2 ∗ M . L=0,R=2*M. L=0,R=2M.

枚举 i ∈ [ 1 , N ] , j ∈ [ 1 , i ] , k ∈ [ 1 , j ] i∈[1,N], j∈[1,i], k∈[1,j] i[1,N],j[1,i],k[1,j],
i i i 员工解决 “软件一” k k k 个模块, 还剩余 l e f t = m i d − D 1 [ i ] ∗ k left = mid - D_1[i]*k left=midD1[i]k 的时间,
转移: F [ i , j ] = m a x ( ⌊ l e f t D 2 [ i ] ⌋ + F [ i − 1 , j − k ] ) F[i,j] = max(\lfloor\frac{left}{D_2[i]}\rfloor+F[i-1,j-k]) F[i,j]=max(D2[i]left+F[i1,jk])

注意 l e f t &lt; 0 left &lt; 0 left<0 或者 F [ i − 1 , j − k ] 无 初 值 F[i-1,j-k]无初值 F[i1,jk] 时无法转移,

最后检查 F [ N , M ] F[N,M] F[N,M] 是否大于等于 M M M 即可.


C o d e \mathcal{Code} Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 105;

int N;
int M;
int Ans;

int D1[maxn];
int D2[maxn];
int F[maxn][maxn];

bool chk(int mid){
        memset(F, -1, sizeof F); F[0][0] = 0;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 0; j <= M; j ++)
                        for(reg int k = 0; k <= j; k ++){
                                int left = mid - D1[i] * k;
                                if(left < 0 || F[i-1][j-k]==-1) continue ;
                                F[i][j] = std::max(left/D2[i] + F[i-1][j-k], F[i][j]);
                        }
        return F[N][M] >= M;
}

int main(){
        scanf("%d%d", &N, &M);
        for(reg int i = 1; i <= N; i ++) scanf("%d%d", &D1[i], &D2[i]);
        int l = 0, r = 10000;
        Ans = 0x3f3f3f3f;
        while(l <= r){
                int mid = l+r >> 1;
                if(chk(mid)) Ans = std::min(Ans, mid), r = mid - 1;
                else l = mid + 1;
        }
        printf("%d\n", Ans);
        return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值