九度笔记之 最大值最小化


题目

题目1502:最大值最小化

时间限制:1 秒

内存限制:128 兆

特殊判题:

提交:131

解决:55

题目描述:

在印刷术发明之前,复制一本书是一个很困难的工作,工作量很大,而且需要大家的积极配合来抄写一本书,团队合作能力很重要。
当时都是通过招募抄写员来进行书本的录入和复制工作的, 假设现在要抄写m本书,编号为1,2,3...m, 每本书有1<=x<=100000页, 把这些书分配给k个抄写员,要求分配给某个抄写员的那些书的编号必须是连续的。每个抄写员的速度是相同的,你的任务就是找到一个最佳的分配方案,使得所有书被抄完所用的时间最少。

输入:

输入可能包含多个测试样例。
第一行仅包含正整数 n,表示测试案例的个数。
对于每个测试案例,每个案例由两行组成,在第一行中,有两个整数m和 k, 1<=k<=m<=500。 在第二行中,有m个整数用空格分隔。 所有这些值都为正且小于100000。

输出:

对应每个测试案例,
输出一行数字,代表最佳的分配方案全部抄写完毕所需要的时间。

样例输入:
2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100
样例输出:
1700
200

分析 

参考http://blog.csdn.net/nanjunxiao/article/details/8145971

第一种方法利用动态规划,比较耗时,耗空间

dp[k][m] 表示k个人前m本书

dp[k][m] = max{ min(dp[ k-1][ i ] ,  sum( pages[i.....m] )}

也就是说多了一个人,多出的一个人处理后面的几本书,前k-1个人处理前面的几本书



另外一种方法

利用二分查找的方法,查找一个最小的x 

该x满足  存在k个连续子序列使每个子序列的和都<= x


代码

// judo1502.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>
int work[501][501]= {0};
//int preWork[501] = {0};
//int curWork[501] = {0};
int pages_book[501] = {0};

void judo(int num_book,int num_people){
	for(int i = 1;i<num_book+1;i++)
		std::cin>>pages_book[i];

	//1个人的工作量
	for(int book_id = 1;book_id<num_book+1;book_id++){
		work[1][book_id] = pages_book[book_id]+work[1][book_id-1];
	}

	//1本书
	for(int People_id = 1;People_id<num_people+1;People_id++){
		work[People_id][1] = pages_book[1];
	}

	int np = 2;
	while(np<=num_people){
		for(int book_id = 2;book_id<num_book+1;book_id++){
			int minwork = work[np-1][book_id];
			int subsum = 0;
			for(int i = book_id;i>1;i--){//第Np个人完成第i本书往后的书
				subsum += pages_book[i];
				int temwork = std::max(work[np-1][i-1],subsum);
				if(temwork<minwork)
					minwork = temwork;
			}
			work[np][book_id] = minwork;
		}
		np++;
	}
	std::cout<<work[num_people][num_book]<<std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int n;
	int k;
	int m;
	while(std::cin>>n){
		for(int i = 0;i<n;i++){
			std::cin>>m>>k;
			judo(m,k);
		}
	}
	return 0;
}

二分查找

// judo1502new.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include<iostream>
int pages[501];
bool canSplit(int num_books,int num_people,int x){
	int s = 0;//每一段的和
	int t = 0;//分割线个数
	for(int i = 0;i<num_books;i++){
		if(pages[i]>x){
			return false;
		}
		if(s+pages[i]>x){
			t++;//添加分割线
			if(t>num_people-1)//分割线已经大于num_people了,所以只有num_people个人不能实现
				return false;
			s = pages[i];
		}else{
			s+=pages[i];
		}
	}
	return true;
}

void judo(int num_books,int num_people ){
	for(int i = 0;i<num_books;i++)
		std::cin>>pages[i];
	int sumpages = 0;
	int maxpages = 0;
	for(int i = 0;i<num_books;i++){
		if(pages[i]>maxpages)
			maxpages = pages[i];
		sumpages+=pages[i];
	}

	int L = maxpages;
	int R = sumpages;
	while(L<R){
		int m = L + ((R-L)>>1);
		if(canSplit(num_books,num_people,m))
			R = m;
		else
			L = m+1;
	}
	std::cout<<L<<std::endl;
}


int _tmain(int argc, _TCHAR* argv[])
{
	int n;
	int k;
	int m;
	while(std::cin>>n){
		for(int i = 0;i<n;i++){
			std::cin>>m>>k;
			judo(m,k);
		}
	}
	return 0;
}




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值