您的位置:首页 > 其它

BSOJ 3425:分班 DP+单调队列

2016-11-08 09:49 260 查看
3425 – 【模拟试题】分班

Description



Input

  本题目有多组数据。第一行有一个整数case(<=10),表示有case组数据。

  接下来对于每组数据的第一行有4个正整数,依次是M,N,A,B。

  第二行有M个正整数,用空格隔开,分别是X[1],X[2] —–X[M]。

  第三行有N个正整数,用空格隔开,分别是G[1],G[2] —–G


Output

  对于每组数据,要输出三个正整数,以空格隔开,不同数据之间要换行。

  三个正整数分别为sigma,class,last。分别表示最小的评价指数。在评价指数最小的情况下,安排的教室的最小数目。在评价指数,安排教师数目最小的情况下,最后一个教室的人数的最小数目。

Sample Input

1

10 3 1 4

16 11 12 13 10 15 16 17 18 14

4 5 1

Sample Output

186 3 4

Hint

【样例解释】



前4个,后4个,中间2个。

求的是sigma((xi-ave)^2)*G[g[i]],ave和xi都是常数,也就将问题简化为一个分配问题。

设f[i][j]为前i个学生,分j班,且当前为第j班的最小评价分数,容易写出DP方程:

f[i][j]=min{f[k][j-1]+G[j]*(sumx[i]-sumx[k])};

(i-B<=k<=i-A)

这是一个O(mn^2)的递推式,只拿得到40分。

考虑优化,使用单调队列,更新f[i][j]的时候一步得到f[k][j-1]-G[j]*sumx[k]的最小值,注意这里用单调队列不仅仅维护f[k][j-1],因为后面还有一项与k有关,故要全部带进去,否则是错误的,对于第2、3问,根据贪心(误?),更小解出现时即更新其答案即可。至此问题得解。

hint:

1. 这里的单调队列只与j-1的各项有关,也即每次j变化,要把头尾指针清零。

2. 递推的方向为上一个班->当前班(j-1->j),可以使用滚动数组,并且DP第一位一定为j。

滚动数组太丑了..考试的时候作死写一个然后惨遭爆零..

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
#define INF 1e18
//#include<cmath>
using namespace std;
inline ll read()
{
ll bj=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')bj=-1;
ch=getchar();
}
ll ret=0;
while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
return ret*bj;
}
ll n,m,a,b,x[10005]={0},G[204]={0},sum[10004]={0},ave=0;
ll f[10001][220]={0},q[100005]={0},head=0,tail=0,minn;
void reset()
{
memset(q,0,sizeof(q));
memset(x,0,sizeof(x));
memset(G,0,sizeof(G));
memset(sum,0,sizeof(sum));
ave=0,minn=INF,head=1,tail=0;
}
void init()
{
reset();
m=read();n=read();a=read();b=read();
for(ll i=1;i<=m;i++)x[i]=read(),ave+=x[i];
for(ll i=1;i<=n;i++)G[i]=read();
ave/=m;
for(ll i=1;i<=m;i++)sum[i]=sum[i-1]+(x[i]-ave)*(x[i]-ave);
}
void DP()
{
ll cla,last,stu;
for(int i=0;i<=10000;i++)for(int j=0;j<=200;j++)f[i][j]=INF;
f[0][0]=0;
for(ll j=1;j<=n;j++)
{
head=1,tail=0;
for(ll i=1;i<=m;i++)
{
if(i-a>=0)
{
if(f[i-a][j-1]!=INF)
{
while(f[i-a][j-1]-sum[i-a]*G[j]<=f[q[tail]][j-1]-sum[q[tail]]*G[j]&&head<=tail)tail--;
q[++tail]=i-a;
}
}
while(q[head]<i-b&&head<=tail)head++;
if(head>tail)continue;
f[i][j]=f[q[head]][j-1]+(sum[i]-sum[q[head]])*G[j];
if(i==m)stu=q[head];
}
if(f[m][j]<minn)
{
minn=f[m][j];
cla=j;
last=m-stu;
}
}
printf("%lld %lld %lld\n",minn,cla,last);
}
int main()
{
ll tim=read();
while(tim--)
{
init();
DP();
}
return 0;
}
/*
1
10 3 1 4
16 11 12 13 10 15 16 17 18 14
4 5 1

1
5 1 4 5
49684 8970 96420 53727 12295
815

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 单调队列