有三个小伙伴组队去参加 ACM 比赛,他们的比赛策略是这样的:每个队员都会对题目通看一遍,然后对每个题的难度进行估算,难度范围为1~5。当然,由于每个队员的水平和特点,他们对同一道题的估算不一定相同。接下来他们会对所有题目进行分配。三个人分配的题目刚好是所有题目,且不会有交集,而且每个人分配的题目的编号必须是连续的,每人至少要分一道题。请问,如何分配题目可以使得三个人拿到的题目的难度之和最小。每个人对自己分配到的题目只按自己的估算值求和。
【输入格式】
第一行一个数n,表示题目的数量。(3<=n<=150000)
接下来有3行。第i+1行有n个数,第j个数表示第i个人对于第j题的估算难度,难度介于1~5。
【输出格式】
一个整数。表示最小的估算难度之和。
【输入样例】
3
1 3 3
1 1 1
1 2 3
【输出样例】
4
【数据范围】
1<=n<150000
【来源】
重庆市NOIP2015模拟赛round1备用题
国庆节6号下午考的T2,结果是一个备用(bei tai)题......噗~
考试的时候一看题目直接懵了,smg啊,为什么不是两个人呢,三个人枚举分隔做题的位置O(N^2)必然超时GG啊,不知道当时怎么想的,太自信的乱设了一个状态,乱写了个方程(觉得自己是对的)交了上去,直接GG了,保底30(听说其实是50)都没有拿到,果然像我这样的蒟蒻实在应该先写个暴力啊~
回到正题上来,设clac(x,y,z)为第x个人做最前面的题,第y个人做中间的题,第z个人做后面的题的最小难度和.
最终答案Ans={clac(1,2,3) ,clac(1,3,2), clac(2,1,3), clac(2,3,1), clac(3,1,2), clac(3,2,1)}
计算clac的时候,设x做编号为1...i的题目,y做i+1...j的题目,z做j+1...n
这里可以暴力枚举i,j,不说了,肯定要超时。
正解的做法是再使用一个f数组,f[i]表示让x,y做前i道题的最小难度和,然后后面计算的时候只用枚举j就行了,这里再设x做前k道题,y就做k+1...i
显然f[i]=min{sum[x][k]+sum[y][i]-sum[y][k] | 1<=k<i }=min{sum[x][k]-sum[y][k] | 1<=k<i }+sum[y][i];而用一个变量维护min{sum[x][k]-sum[y][k] | 1<=k<i }就可以让f[i]的计算变成O(n)
贴上代码(这几次test总感觉自己NOIP会GG)
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cctype>
#define oo 2000000000
using namespace std;
const int maxn = 150005;
int n;
int a[4][maxn],sum[4][maxn];
int f[maxn];//f[i]表示让x,y做前i道题的最小难度和
//f(i)=min{sum[x][j]+sum[y][i]-sum[y][j] | 1<=j<i};
//数学变换得
//f(i)=min{sum[x][j]-sum[y][j] | 1<=j<i}+sum[y][i];
//设minv=min{sum[x][j]-sum[y][j] | 1<=j<i}
//f(i)=minv+sum[y][i];
//clac(x,y,z)=min{f(i)+sum[z][n]-sum[z][i] | 2<=i<n}
void _read(int &x)
{
x=0;
char ch;
ch=getchar();
while(ch>'9' || ch<'0')ch=getchar();
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
}
void ready()
{
memset(sum,0,sizeof(sum));
for(int i=1;i<=3;i++)
for(int j=1;j<=n;j++)
sum[i][j]=sum[i][j-1]+a[i][j];
}
int clac(int x,int y,int z)
{
//for(int i=1;i<=n;i++)f[i]=oo;
f[2]=sum[x][1]+sum[y][2]-sum[y][1];
int minv=min(sum[x][1]-sum[y][1],sum[x][2]-sum[y][2]);
for(int i=3;i<n;i++)
{
f[i]=minv+sum[y][i];
minv=min(sum[x][i]-sum[y][i],minv);
}
int ans=oo;
for(int i=2;i<n;i++)
{
ans=min(ans,f[i]+sum[z][n]-sum[z][i]);
}
return ans;
}
int main()
{
//freopen("acm.in","r",stdin);
//freopen("acm.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=3;i++)
for(int j=1;j<=n;j++)
{
_read(a[i][j]);
}
ready();
int Ans=oo;
Ans=clac(1,2,3);
Ans=min(Ans,clac(1,3,2));
Ans=min(Ans,clac(2,1,3));
Ans=min(Ans,clac(2,3,1));
Ans=min(Ans,clac(3,1,2));
Ans=min(Ans,clac(3,2,1));
printf("%d",Ans);
return 0;
}