书的复制
一道二维DP题,但是需要用到贪心。
看题面:
洛谷P1281 书的复制
题目背景
大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出 0 0
。
不过,已经修改数据,保证每个人都有活可干。
题目描述
现在要把 m 本有顺序的书分给 k 个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。
现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
输入格式
第一行两个整数 m,k。
第二行 m 个整数,第 i 个整数表示第 i 本书的页数。
输出格式
共 k 行,每行两个整数,第 i 行表示第 i 个人抄写的书的起始编号和终止编号。 k 行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。
输入输出样例
输入 #1
9 3 1 2 3 4 5 6 7 8 9
输出 #1
1 5 6 7 8 9
说明/提示
1≤k≤m≤500。
首先,让前面的人抄书尽量少的前提是抄书最多的人抄书最少,所以让前面的人抄书尽量少并不会对抄书最多的人的抄书最小值产生影响,所以只需要用DP求出抄书最多的人的抄书最小值,然后用贪心让前面的人抄书尽量少就可以了。
DP:一个二维数组f[i,j]代表前i个人抄了前j本书,枚举前i-1个人抄了前k本书,取f[i-1,k]+(k+1到j的书籍总页数)的最小值。
贪心:从后往前在不超过抄书最小值的情况下是后面的人抄书更多,但也要保证每个人都有书抄。
看代码:
#include<iostream> #define INF 0x3f3f3f using namespace std; int read(){ int x=0,f=1; char a=getchar(); while(a<'0'||a>'9'){ if(a=='-')f=-1; a=getchar(); } while(a>='0'&&a<='9'){ x*=10; x+=a-'0'; a=getchar(); } return x*f; } int m,k; int a[510]; int f[510][510]; void dfs(int i,int j,int t){ if(j==0)return ; if(i<j||f[1][t]-f[1][i-1]>f[k][m]){ dfs(i,j-1,i); cout<<i+1<<" "<<t<<endl; return ; } dfs(i-1,j,t); } int main(){ m=read();k=read(); for(int i=1;i<=m;i++){ a[i]=read(); f[1][i]=f[1][i-1]+a[i]; } for(int i=2;i<=k;i++){ for(int j=i;j<=m;j++){ f[i][j]=INF; for(int l=i-1;l<=j-1;l++){ f[i][j]=min(f[i][j],max(f[i-1][l],f[1][j]-f[1][l])); } } } dfs(m,k,m); return 0; }