对于抄书问题1,由于标签上写的DP,我就写了DP。。。。
设dp[I][j]表示前i本书由j个人抄的最小答案,则状态转移方程为
dp[I][j]=min{max(dp[k][j-1],s[I]-s[k])},其中s[]为前缀和,k从j-1到1枚举。
输出方案时,由于题目要求多解时使前面的人抄的尽量少,因此可以贪心地输出。
由于我们在前面的DP过程后已经知道了每个人抄书的页数的最大值的最小值dp[m][k],因此可以逆序枚举第i个人,将不超过dp[m][k]的书都交给i抄,然后得出的答案保证前边的人抄书尽可能少,这样就能AC抄书问题1了。时间复杂度
O(k*m^2+k);
#include<iostream>
#define maxn 105
#define inf 0x7fffff
using namespace std;
int m,k,book[maxn],s[maxn];
struct Anses{
int from,to;
};
int dp[maxn][maxn];
int main(){
ios::sync_with_stdio(false);
cin>>m>>k;
for(int i=1;i<=m;i++){
cin>>book[i];
s[i]=s[i-1]+book[i];
dp[i][1]=s[i];
}
for(int i=2;i<=m;i++){
for(int j=2;j<=k&&j<i;j++){
int minx=inf;
for(int k=i-1;k>=1;k--){
int maxx=0;
if(i-k<j-1)continue;
if(maxx<dp[i-k][j-1]) maxx=dp[i-k][j-1];
if(maxx<s[i]-s[i-k]) maxx=s[i]-s[i-k];
if(minx>maxx) minx=maxx;
}
dp[i][j]=minx;
}
}
int ansxx=dp[m][k];
Anses ans00[maxn];
int i=m,k0=k;
while(k0){
int j=i-1;
while(s[i]-s[j]<ansxx&&j>=0) j--;
if(s[i]-s[j]==ansxx){
ans00[k0].from=j+1;
ans00[k0].to=i;
k0--;
i=j;
}
else if(s[i]-s[j+1]<=ansxx){
ans00[k0].from=j+2;
ans00[k0].to=i;
k0--;
i=j+1;
}
}
for(int i=1;i<=k;i++)cout<<ans00[i].from<<' '<<ans00[i].to<<endl;
return 0;
}
但是对于抄书问题2&3,以上DP显然不行,因此得换思路。
刚才的讨论中,DP过程的作用,是求出m本书分成k份的最大代价的最小值,之后贪心地输出就可以了,是不是有二分答案的味道?
我们可以二分查找最大价值的最小值,然后用刚刚所得的贪心策略验证,如果分给了>k个人,说明二分的答案偏小,反之偏大。
但是codevs的数据神坑。。。。。
由于我们刚才的贪心策略,有可能根本用不了K个人就能在最优策略下抄完m本书,此时程序将前边的人分配了0本书!但是根据codevs上的数据,应该是每个人都至少抄一本书,呵呵。。。。
#include<iostream>
#define maxn 1000000+5
using namespace std;
int s[maxn],n,k,begin[maxn],end[maxn];
int main(){
ios::sync_with_stdio(false);
cin>>n>>k;
if(!k)return 0;
for(int i=1;i<=n;i++){
cin>>s[i];
s[i]+=s[i-1];
}
int lf=1,ri=s[n],ans,mid;
for(int i=1;i<=k;i++)begin[i]=end[i]=i;
while(lf<ri){
drg:;
mid=(lf+ri)>>1;
int b=n,e=n,cnt=0;
while(b>=0){
if(s[b]-s[b-1]>mid){
lf=mid+1;
goto drg;
}
if(s[e]-s[b-1]<=mid) b--;
else{cnt++;e=b;}
}
cnt++;
if(cnt>k) lf=mid+1;
else ri=mid;
}
int b=n,e=n,mark=0;
for(int i=k;i>=1;i--){
b=e;
while(s[e]-s[b-1]<=ri){
if(b==i){
end[i]=e;
for(int i=1;i<=k;i++)cout<<begin[i]<<' '<<end[i]<<endl;
return 0;
}
b--;
}
end[i]=e;
begin[i]=b+1;
e=b;
}
}