【二分】SOFTWARE

1. SOFTWARE

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

【输入】

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

【输出】

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

【样例】

 SOFTWARE.IN

  3 20

  1 1

  2 4

  1 6

  SOFTWARE.OUT

  18

【样例】

最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。


初看总觉得是动态规划。但是最后还是看了题解才知道是二分。我想从题中分析还是能找到蛛丝马迹否定动态规划的解法。

如果用动态规划,将会很难划分阶段,不可能以时间划分,因为同一时间可以有多个任务进行,除非人的数量是固定的,则可以把同一时刻的每人任务作为一维。任务是同时的,来划分阶段不现实,用人也不行。

容易看出来,主要矛盾就在于可以同时进行的任务。


而有利的是,这些任务是不分先后的,不管以什么样的顺序来做都是一样的,同时,一类任务只有做得多少的区别,没有谁做和做哪一件的区别,所以我们可以看出来,每一个工人之间关系是非常独立的。。。最后的答案,木桶原理,是从所有的人中找一个时间最长的,因此,我们要做的是,让每一个人分别满足要求。。。字里行间里已经看见,我们是要最大值最小,因此想到二分。


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


提交次数略多。。


#include <cstdio>
#include <cstring>
#include <string>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))

long mid;
long left[2];
long a1[1000];
long a2[1000];
long n,m;
char f[110][110][110];
long getint()
{
	long rs=0;char tmp;bool sgn=1;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){sgn=0;tmp=getchar();}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

bool can(long l)
{
	if (f[l][left[1]][left[2]])
		return false;
	if (l == n)
	{
		if (left[1]*a1[l]+left[2]*a2[l] <= mid)
			return true;
		else
		{
			f[l][left[1]][left[2]] = true;
			return false;
		}
	}
	for (long i=0;i*a1[l]<=mid&&i<=left[1];i++)
	{
		long j = (mid-i*a1[l])/a2[l];//尽量多做,把时间安排紧
		if (j > left[2])//不能把任务做成负数个了
			j = left[2];
		left[1] -= i;
		left[2] -= j;
		if (can(l+1))
			return true;
		left[1] += i;
		left[2] += j;
	}
	f[l][left[1]][left[2]] = true;
	return false;
}

int main()
{
	freopen("software.in","r",stdin);
	freopen("software.out","w",stdout);
	n = getint();
	m = getint();

	for (long i=1;i<n+1;i++)
	{
		a1[i] = getint();
		a2[i] = getint();
	}
	if (n == 1)
	{
		printf("%ld",m*a1[1]+m*a2[1]);
		return 0;
	}

	long l = 0;
	long r = MIN(MAX(m*a1[2],m*a2[1]),MAX(m*a1[1],m*a2[2]));
	long ans = 0x7f7f7f7f;
	
	while (l <= r)
	{
		mid = (l+r)>>1;
		left[1] = m;
		left[2] = m;
		memset(f,0,sizeof f);
		if (can(1))
		{
			if (mid < ans)
				ans = mid;
			r = mid-1;
		}
		else
		{
			l = mid+1;
		}
	}
	printf("%ld",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值