【题解】SOFTWARE 二分+搜索/dp

题目描述

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

输入输出格式

输入格式:

输入文件第一行包含两个由空格隔开的整数n和m,接下来的n行每行包含两个用空格隔开的整数d1和d2,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数。

输出格式:

输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。

输入输出样例

输入样例#1: 复制

3 20
1 1
2 4
1 6

输出样例#1: 复制

18

说明

1<=n<=100,1<=m<=100。 1<= d1,d2<=100。

思路

  • 最大值最小(要求做得最慢的人越早做完),因此想到二分

dp

  • 设mid天后交付;
  • $f[i][j]$表示当前i个人共完成了j个模块一时,还能完成多少个模块二
  • 设第i个人完成了k个模块一,则在剩下的时间内他还可以完成$(mid-k*d1[i])/d2[i]$个模块二
  • 可以得到转移方程

$$f[i][j]=max(f[i][j],f[i-1][j-k]+((mid-k*d1[i])/d2[i]))$$

  • 最后检验计较$f[n][m]$与m大小(当n个人完成了m个模块一时,能否完成m个模块二)

搜索

  • 设mid天后交付;
  • 按人员编号进行搜索,用lft[0],lft[1]表示还有几个模块一,模块二需要完成
  • 枚举第i个人做了几个模块一,在通过$(mid-k*d1[i])/d2[i]$算出可以在剩下几天内在做几个模块二
  • lft[0],lft[1]分别减去第i个人完成的模块一,二数量=>进行下一个人的搜索
  • 当i=n时,自然剩下所有的未完成的lft[0],lft[1]都要他完成
  • 计算此时$d1[i]*lft[0]+d2*lft[1]$的大小与mid的关系

这个二分的DFS检验其实是可以记忆化的,如果是成功的就直接退出了,但是不成功的没有退出,但是会重复计算,比如前3个人,模块一还剩下3个,模块二剩下4个,这个f(3,3,4)可能由很多的状态扩展过来,因此会有很多重复计算,不过要记得每次检验之前要清空f

  • 可以用$f[cnt][lft[0]][lft[1]]$记下此方案是否可行,下次再搜到就直接return false

代码

#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register int
using namespace std;
inline int read(){
    int x=0,w=1;
    char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) w=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    return x*w;
}
int lft[2];     //还有多少模块要完成
int a1[1000],a2[1000];
int mid,n,m;
char f[110][110][110];
bool chek(int cnt) {     //cnt--人
    if (f[cnt][lft[1]][lft[2]]) return false;    //表示前cnt个人 剩下lef[1] 和lef[2]时不可行
    if (cnt==n) {
        if (lft[1]*a1[cnt]+lft[2]*a2[cnt] <= mid)
            return true;
        else {
            f[cnt][lft[1]][lft[2]]=true;
            return false;
        }
    }
    for (re i=0;i*a1[cnt]<=mid&&i<=lft[1];i++) {
        int j=(mid-i*a1[cnt])/a2[cnt];   //可做的a2数
        if (j>lft[2]) j=lft[2];
        lft[1]-=i;
        lft[2]-=j;
        if (chek(cnt+1)) return true;
        lft[1]+=i;
        lft[2]+=j;
    }
    f[cnt][lft[1]][lft[2]] = true;
    return false;
}

int main() {
    freopen("T21331.in","r",stdin);
    freopen("T21331.out","w",stdout);
    n=read(),m=read();
    for (re i=1;i<n+1;i++){
        a1[i]=read();
        a2[i]=read();
    }
    if (n == 1) { printf("%d",m*a1[1]+m*a2[1]); return 0; }
    int cnt=0;
    int r=min(max(m*a1[2],m*a2[1]),max(m*a1[1],m*a2[2]));
    int ans=0x7f7f7f7f;
    while (cnt<=r) {
        mid=(cnt+r)>>1;
        lft[1]=m;
        lft[2]=m;
        memset(f,0,sizeof f);
        if (chek(1)){
            ans=min(mid,ans);
            ans = mid;
            r=mid-1;
        }
        else cnt=mid+1;
    }
    printf("%d",ans);
    return 0;
}

搜索

#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register int
using namespace std;
const int inf=2147483647;
const int N=110;
inline int read(){
    int x=0,w=1;
    char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) w=-1,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    return x*w;
}
int f[N][N],d[N][2],n,m,t;
int main() {
    freopen("T21331.in","r",stdin);
    freopen("T21331.out","w",stdout);
    re i,j,k;
    n=read(),m=read();
    for(i=1;i<=n;++i) d[i][0]=read(),d[i][1]=read();
    int l=0,r=100000;
    int mid;
    while (l<r) {
        mid=(l+r)>>1;
        for(i=0;i<=n;++i) for(j=0;j<=m;++j) f[i][j]=-inf;
        f[0][0]=0;
        for(i=1;i<=n;++i)
            for(j=0;j<=m;++j)
                for(k=0;k<=min(j,mid/d[i][0]);++k)
                    f[i][j]=max(f[i][j],f[i-1][j-k]+(mid-d[i][0]*k)/d[i][1]);
        if(f[n][m]>=m) r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    return 0;
}

dp

?

原文地址:https://www.cnblogs.com/bbqub/p/8482806.html

时间: 2024-10-13 12:25:54

【题解】SOFTWARE 二分+搜索/dp的相关文章

Codeforces Round #460 (Div. 2) B Perfect Number(二分+数位dp)

题目传送门 B. Perfect Number time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output We consider a positive integer perfect, if and only if the sum of its digits is exactly 1010. Given a positive integ

hdu 4960 记忆化搜索 DP

Another OCD Patient Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 490    Accepted Submission(s): 180 Problem Description Xiaoji is an OCD (obsessive-compulsive disorder) patient. This morni

【题解】剪纸条(dp)

[题解]剪纸条(dp) HRBUST - 1828 网上搜不到题解?那我就来写一篇吧哈哈哈 最优化问题先考虑\(dp\),设\(dp(i)\)表示将前\(i\)个字符(包括\(i\))分割成不相交的回文子串的最小数目 直接模拟题意转移即可.初始化写在里面了,\(dp(i)=i\) \[ dp(i)=\min\{i,dp(j-1)\} \] 其中\(S[j\dots i]\)是一个回文串,\(O(n^2)\)预处理回文串即可,注意偶回文串和奇回文串. 刚开始想太多,这道题.以后遇最优化的题一定要花

hdu1331&amp;&amp;hdu1579记忆化搜索(DP+DFS)

这两题是一模一样的``` 题意:给了一系列递推关系,但是由于这些递推很复杂,所以递推起来要花费很长的时间,所以我要编程序在有限的时间内输出答案. w(a, b, c): 如果a,b,c中有一个值小于等于0,那么w(a, b, c)的值为1 如果a,b,c中有一个值大于20,那么w(a, b, c)的值为w(20, 20, 20) 如果a<b<c,那么w(a, b, c)=w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c) 否则w(a, b, c)=w(a-

最长斐波那契子序列选取(离散化 + 二分 + DP)

[题目]: 如果对于所有的i = 3,4,..,n,有 ai =  ai-1+ ai-2, 那么整数序列a1,a2,...,an 就被称作Fibonacci数列. 给出一个整数数列 c1, c2, ..., cm,你需要找出这个数列里的最长Fibonacci子序列(注意,子序列不能改变给出的整数数列顺序). 输入 输入数据第一行包含一个整数m(1 <= m <= 3,000).其后一行有m个整数,这些整数的绝对值不超过10^9.  输出 仅输出一个整数,表示输入数据给出的序列中的最长Fibon

HDU 6041 I Curse Myself(二分+搜索)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6041 [题目大意] 给出一个仙人掌图,求第k小生成树 [题解] 首先找到仙人掌图上的环,现在的问题就是从每个环中删除一个元素, 求出删除元素总和中的第K大,我们发现通过限定第K大的大小,可以有效地搜索剪枝, 限制的大小导致搜索出来的总和数量是具有单调性的,我们可以二分这个值, 然后用搜索来定位第K大的大小.Thanks to Claris. [代码] #include <cstdio> #in

BZOJ2246 [SDOI2011]迷宫探险 【记忆化搜索dp + 概率】

题目 输入格式 输出格式 仅包含一个数字,表示在执行最优策略时,人物活着走出迷宫的概率.四舍五入保留3位小数. 输入样例 4 3 3 2 .$. A#B A#C @@@ 143 37 335 85 95 25 223 57 输出样例 0.858 提示 题解 毒瘤dp题 我们设\(f[x][y][s][h]\)表示从点\((x,y)\)出发,所有陷阱状态为\(s\),生命值为\(h\),存活的期望概率 我们枚举邻点,选择存活概率最大的作为当前\(f\)的值 除了墙,有以下情况: ①如果是空地或者终

codevs 3342 绿色通道 (二分+线性DP)

codevs 3342 绿色通道 http://codevs.cn/problem/3342/ 难度等级:黄金 题目描述 Description <思远高考绿色通道>(Green Passage, GP)是唐山一中常用的练习册之一,其题量之大深受lsz等许多oiers的痛恨,其中又以数学绿色通道为最.2007年某月某日,soon-if (数学课代表),又一次宣布收这本作业,而lsz还一点也没有写…… 高二数学<绿色通道>总共有n道题目要写(其实是抄),编号1..n,抄每道题所花时间

HDU 1078 FatMouse and Cheese(记忆化搜索DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1078 题目大意:一个n*n的图,每个点都有奶酪,老鼠从(0,0)开始走,每次最多只能走k步就要停下来,停下的这个位置的奶酪数只能比上一个停留的位置大,并获取其奶酪,每次只能水平或垂直走,问最多能得到的奶酪. 解题思路:记忆化搜索,这方面还是写的太少,还要看别人才会,这个就当个例子参考吧. 1 #include<cstdio> 2 #include<cstring> 3 #include