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;
}