欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

luogu1281:书的复制:区间DP+贪心

程序员文章站 2022-05-14 18:17:37
...

最近在复习DP,从最傻的题目开始敲,再次发现自己的失意症;去luogu重刷的时候,很惊奇低发现,原来最后一篇题解还是自己写(chao)的,当时还非常嘚瑟写了一个二分的一题多解,66666


题目连接:

题目大意:(本题有2问)

1 有n本连续的书,需要分给m个人,同时抄写;

2 要求每个人都需要抄连续编号的书,总时间最少;

3 第一问: 其实就是“分到最多书的人,页数尽可能少”

4 第二问:要求前面的人尽可能少抄,输出每个人的得书情况;


解题思路:

1 DP部分:问什么设什么:f[i][j]表示:前i本书分给前j个人,抄写的时间最短;

2 枚举最后一个人能分到k本书;

3 转移方程:f[i][j]=min(f[i][j],max(f[i-k][j-1],s[i]-s[i-k-1]));//别怂,下面是详解↓

3.1 转移方程拆分1: max(f[i-k][j-1],s[i]-s[i-k-1]) 

3.1.1 当前书一共有i本,最后一个人(第j个人)抄k本,所以前(j-1)个人,抄(i-k)本,前面(j-1)个人抄(i-k)本书的时间是f[i-k][j-1];最后一个人抄书的时间是:s[i]-s[i-k-1],两者取最大值(因为是同时抄的);

3.2 转移方程拆分2:min(f[i][j],xxx);//枚举求最小值,能看懂吧,

3.3 到这里,DP部分结束了,能算出最少的用时,但是不知道每个人具体 能分派到哪一段的书;

4 贪心部分:因为题目说:“前面的人尽可能少抄”,因为每个人需要抄的最大值,已经求出来了,所以从后往前扫一遍,就可以了。


上代码:

#include<bits/stdc++.h>
using namespace std;

int n,m,t=0;
int a[510],s[510];
int b[510],f[510][510];

void dp()
{
	for(int j=2;j<=m;j++)//第j个人 
	{
		for(int i=j;i<=n;i++)//前i本书 
		{
			for(int k=j;k<=i;k++)//最后一个人拿后k本 
			{
				int ans=0;
				ans=max(f[k-1][j-1],s[i]-s[k-1]);//详解请看:3.1 
				f[i][j]=min(f[i][j],ans);//3.2
			}
		}
	}
}
void pr()
{
	b[++t]=n;//b[t]:第t个人抄的最后一本书的编号 
	int su=0;
	for(int i=n;i>=1;i--)//从后往前扫一遍 
	{
		if(su+a[i]>f[n][m])
		{
			b[++t]=i;
			su=a[i];
		}
		else su+=a[i];
	}
	printf("%d %d\n",1,b[t]);//第一个人要单独输出
	 
	for(int i=t;i>1;i--) printf("%d %d\n",b[i]+1,b[i-1]);
}

int main()
{
	s[0]=0;
	memset(f,63,sizeof(f));
	
	scanf("%d %d",&n,&m);//n本书分给m个人;
	if(n==0) return 0;
	
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		f[i][1]=s[i]=s[i-1]+a[i];
	}
	
	dp();//DP部分 

	pr();//利用贪心输出 
	
	
	return 0;
}

 

相关标签: DP 题解 洛谷