题目描述:2015 年 UOI 马上就要开始了,CSSYZ2015 信息队派出最强三位同学组成“宇宙队”参加这次 ACM 比赛,周老师想让他们以最优的策略去完成比赛,于是,将题目分为 5 等,编号 1 到 5,数字越大,难度越大。对于每一个同学,同一道题可能难度不一样,现在,周老师想知道,如何安排学生做题顺序,能使难度值总和最低。注意:每位同学只能做编号连续的题目,并且不能一道题不做。
输入:
第一行一个整数 N(3<=N<=150000),为题目数量。
第二行到第四行,每行 N 个整数,代表每个题目对于三名同学的难度。
输出:
一行,最小难度总和。
样例输入1:
3
1 3 3
1 1 1
1 2 3
样例输入2:
7
3 3 4 1 3 4 4
4 2 5 1 5 5 4
5 5 1 3 4 4 4
样例输出1:
4
样例输出2:
19
题目思路:
- 首先大家都能够想到这道题能转化。因为每个人至少做一道题,且做的题目编号要连续 (这个编号不是难度编号,是输入顺序的编号。别问我怎么知道的,我是不会告诉你我因这个细节WA了无数次的)。所以,我们就能知道有一个人做了从第一个题开始的若干个题,还有一个人做了以最后一道题结尾的若干个题,剩下的就是第三个人做了。
画张图形象的描述一下。
//图
所以我们发现,题目被i和j(i+1<j)两个点分成了三部分,这道题就被巧妙地转化成了:先将三个人全排列,确定哪个人来做那一块连续的题;对于每一种排序,找到所有i和j,使其满足i+1<j,然后求难度和,然后取 min,得出最小难度总和。
- 接下来就可以暴搜了!!! 望了一眼数据,本以为已经取得胜利的我,原来还在起跑线上。
既然暴力不行,就试试其他办法,再仔细看看我们目前的线索,有一个点十分引人注目:有一个人做了从第一个题开始的若干个题,还有一个人做了以最后一道题结尾的若干个题。
左青龙,右白虎,前朱雀,后玄武,中间一个250.
呸!呸!一个在前,一个在后。可以用前缀和与后缀和来优化呀!
- 然后我们就可以惊奇的发现,没有用。
但是我们得到一个结论:我们需要从两边进行考虑。这时,我们就可以假设左边第一块题为0,右边第一块也为0,也就是中间那个人需要写所有的题目,但是其他人也要写题啊,所以就可以开始扩展。
Ans(难度和) a[ i ] (对于第1个人来说第i道题的难度)
b [ i ] (对于第2个人来说第i道题的难度)
c [ i ] (对于第3个人来说第i道题的难度)
那么,未扩展前,Ans=b[1]+b[2]+…+b[n];
如果要将前i道题给第一个人做,那么第1 ~ i道题的难度就由b[1 ~ i]变为a[1~i]。
所以可得出Ans=b[1]+b[2]+…+b[n]+(a[1]-b[1])+(a[2]-b[2])+…+(a[i]-b[i]);
同理,如果要将后j道题给第三个人做,那么第j~n道题的难度就由b[j~n]变为了c[j~n];
所以在上述的情况下可得出结论Ans=b[1]+b[2]+…+b[n]+(a[1]-b[1])+(a[2]-b[2])+…+(a[i]-b[i])+(c[j]-b[j])+(c[j+1]-b[j+1])+…+(c[n]-b[n]);
我们再用用前缀和与后缀和优化一下,定义两个数组。
f [i] (a[1~i]-b[1~i]的和) g [j] (c[j~n]-b[j~n]的和)
最终,Ans=b[1~n]+(f [i]+g [j]);(i+1<j,因为第二个人至少得做一道题)
也就是说,我们要使Ans最小,就要让b[1~n]+(f [i]+g [j])最小,但是b[1~n]是一直不会改变的,所以只要使f [i]+g [j]最小就行了。
- 然后,我们就又能够惊奇的发现,这还不是找i和j吗?不还是O(n*n)的复杂度吗?
别着急,我们可以只枚举j,而i<j-1的,要使f [i]+g [j]最小,因为g [j]是固定的,所以我们就可以让f [i]最小就行了,也就是f [1~(j-2)]中最小的那个;这样,我们先求出f [1~n],然后定义另一个数组x [i]表示为在f [1~i]中最小的。
这样,枚举j,Ans=min(Ans, b[1~n]+(x[j-2]+g[j]));
- 这样就可以
简简单单地A掉这道题了。
代码如下:
#include<bits/stdc++.h>
#define R register int
using namespace std;
int ans=1000000007, n;
int pos[4][150005];
int f[150005];
int g[150005];
int m[4];
void work(int a, int b, int c){
for(R j=1;j<=n;++j) f[j]=f[j-1]+pos[a][j]-pos[b][j];
for(R j=n;j>=1;--j) g[j]=g[j+1]+pos[c][j]-pos[b][j];
for(R j=2;j<=n;++j) f[j]=min(f[j], f[j-1]);
for(R i=n;i>=3;--i)
ans=min(ans, m[b]+f[i-2]+g[i]);
}
int main (){
freopen("acm.in", "r", stdin);
freopen("acm.out", "w", stdout);
scanf ("%d", &n);
for(R i=1;i<=3;++i)
for(R j=1;j<=n;++j){
scanf ("%d", &pos[i][j]);
m[i]+=pos[i][j];
}
work(1, 2, 3);
work(1, 3, 2);
work(2, 1, 3);
work(2, 3, 1);
work(3, 1, 2);
work(3, 2, 1);
printf("%d\n", ans);
return 0;
}
谢谢观看,蒟蒻有不对的地方,还请多多指教。