【USACO4.2.3】工序安排 附带官方题解翻译

先考虑第一问:


贪心。如何贪心呢? 看成时间块的插入。 插入1号机器的话,时间条长度为a[1], 插入2号机器,时间条长度为a[2].

类似俄罗斯方块一样~~! 然后想办法插入,使得最高的那个最低。


x[i]表示,当前插了一些时间条后,第i个机器的时间条高度。 (看成俄罗斯方块!插时间条,也就是只有俄罗斯方块的很长的那个长条。) x[i]就是当前第i个位置的高度(俄罗斯方块是有高度的……)


显然对于i机器而言,每次插入的时间条长度都是a[i] 


要插一个时间条,穷举一下所有可以插的位置,看看插入哪个位置后,高度最低。 也就是 {x[i] + a[i]}最小,就插在i的位置。

代码如下: m1表示有m1个机器

inline int get(int a[], int x[], int m1, int q1[])
{
	int ans1(0);
	for (int i = 1; i <= n; ++ i)
	{
		int tmp = 0x7fffffff, pos1, pos2;
		for (int j = 1; j <= m1; ++ j)
			if (a[j] + x[j] < tmp)
			{
				tmp = a[j] + x[j];
				pos1 = j;
			}	
		x[pos1] = tmp;
		q1[i] = tmp;
		if (tmp > ans1)	ans1 = x[pos1];
	}
	return ans1;
}


==========

官方题解翻译:

你可以贪婪的决定第k个job可以被A机器完成的最早的时间。(显然这题就是贪心啦!)
对于每个机器,时刻注意它什么时候完成了它正在做的任务。(显然,一完成一个任务,立马就投入下一个任务啊……)


完成接下来工作所需要的时间,是当前已经工作的时间加上对于这个机器而言,处理一次所要花费的时间.


如何决定第k个工作在哪个机器呢?  选择能最先完成它的机器! 然后给这个机器安排第k个工作。接着更新这个机器已经工作的时间。

同样的,决定第k个工作什么时候从被A机器完成,到可以被放入B机器。 对于B机器而言,最先完成的工作是从所有工作结束的地方开始测量(这句话翻译的有点奇怪,换一个方式表达: 我

们可以假设在T时间完成所有任务, 那么我们就让每个工作在B机器都在第T时间恰好完成)。这类似于对于A而言翻转过来的过程,每一个被A机器完成的工作,还必须被B机器“拆解”。


一旦这2个计算已经被计算完,并且数组被排序完成后,你最终得到的图片应该和这个类似:
【不懂?其实先看图,再看图后面的文字。再加强理解】










每一行代表一个工作的行为。绿色和蓝色是A机器,黄色,蓝绿色(cyan),紫色代表B的机器。一个红色的行表示这一段时间这个工作没有进行。左边部分对应A工作,在每一行末尾的地方,

是第k个工作完成的时间。(图对应样例数据)
右边部分对应B工作,每一行最开始的地方是 第K个工作最早的可以在B工作结束的时候开始。白色的空格代表空闲(slack)时间,这个时间工作都在运转中。

最好的选择是让最早完成A工作,匹配上最迟完成B工作,第二个完成A工作的,匹配第二迟完成B工作的。在这些时间中取得最大值。这对应让图两边的图形移动,相碰撞。(有一个工作没有空闲(slack)时间)


我的方法:我没有用白色空格的时间,我是穷举结束的时间,然后判定是否合法- - 之前没看题解,没想到到直接碰撞的方法。

/*
TASK:job
LANG:C++
*/
#include <cstdio>

int n;
int m1, m2;
int a[50]={0}, b[50]={0};
int x[50]={0}, y[50]={0}, tot=0;
int q1[1010], q2[1010];

void init()
{
	scanf("%d%d%d", &n, &m1, &m2);
	for (int i = 1; i <= m1; ++ i)	scanf("%d", &a[i]);
	for (int i = 1; i <= m2; ++ i)	scanf("%d", &b[i]);
}

inline int get(int a[], int x[], int m1, int q1[])
{
	int ans1(0);
	for (int i = 1; i <= n; ++ i)
	{
		int tmp = 0x7fffffff, pos1, pos2;
		for (int j = 1; j <= m1; ++ j)
			if (a[j] + x[j] < tmp)
			{
				tmp = a[j] + x[j];
				pos1 = j;
			}	
		x[pos1] = tmp;
		q1[i] = tmp;
		if (tmp > ans1)	ans1 = x[pos1];
	}
	return ans1;
}

inline bool check(int k)
{
	int j = n;
	for (int i = 1; i <= n; ++ i)
	{
		int tmp = q1[i];	
		if (k - q2[j] < tmp)	return false;
		--j;
	}
	return true;
}

void doit()
{
	int ans1, ans2;
	ans1 = get(a, x, m1, q1);
	ans2 = get(b, y, m2, q2);
	printf("%d ",ans1);
	int L = 0,R = 14000, mid;
	while (L + 1 < R)
	{
		mid = (L + R) / 2;
		if (check(mid))	 R = mid;
		else L = mid;
	}
	printf("%d\n",R);
}

int main()
{
	//freopen("job.in","r",stdin);
	//freopen("job.out","w",stdout);
	init();
	doit();
	return 0;
}
/**

2 2 2
3 5
8 5

*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值