问题分析:
虽然不能严谨的证明,但是其实自己模拟几组数据再操作一下就发现,无论怎么操作只要每个人的愿望座位定下来,总怒气值就是定值,都不会变(只不过看是谁生气而已 笑)。
其实最简单的或者说最容易想到的方法就是按照题目的说法,用一个数组来记录位置是否被占用,占用就检索下一个是否被占用,最后用下标相减就是怒气值。
但这个算法的时间复杂度可是O(N^2)呀!我们又看到了题目熟悉的0<m<=n<=100000
机智的我又看了一下提交的情况,一半都是TLE超时......说明这个算法不太行。
emmmm也许你和我一样在比赛时一下子失去了方向。
现在比完静下心来思考,其实在证明出无论如何安排总怒气值不变后,难度就降低很多了,你需要考虑的就是如何去计算出结果而已。
我们就按照椅子的顺序来考虑吧,因为每个嘉宾要么直接做到心仪的位置,要么直接往后走,不存在往前走的情况,那么我们就可以设置一个endflag来表示到目前这个椅子为止,坐的最右边的嘉宾的位置。然后后面先判断与endflag的大小关系,再决定是否要计算angry和更新endflag 的方法。
AC代码:
#include <bits/stdc++.h> using namespace std; int n[100010]; int main() { int total,guest; int t; int endflag=0; //一定要ll不然会WA long long angry=0; cin >> total >> guest; //用n数组标记每个椅子有多少嘉宾喜欢 for ( int i=1; i<=guest; ++i ) cin >> t, n[t]++; for ( int i=1; i<=total; ++i) { //没有嘉宾喜欢的椅子就不用判断 if ( n[i] ) { //若大于endflag说明这个位置还没有人做 可以占用 if ( i>endflag ) { if ( n[i]==1 ) endflag = i; //如果很多人同时喜欢这个位置,那么根据人数更新endflag同时计算angry else { endflag = i+n[i]-1; t = 1; for ( int j=1; j<=n[i]-1; ++j ) angry += t++; } } else { //若小于,说明这个位置之前的位置肯定已经做满了 //因为我们是按照椅子的顺序进行遍历的 //所以这个时候我们就要用另一种方式更新endflag和angry endflag += n[i]; t = endflag; for ( int j=1; j<=n[i]; ++j ) angry += (t-i),t--; } } } if ( endflag > total ) cout << "-1"; else cout << angry; }
如果上面更新endflag和angry的计算方法你还是有点不懂,推荐你以
10 8 1 4 4 5 5 6 6 6
的例子自己在草稿纸上写写就会明白的~
当然,我这个还是太过冗长,可以优化。
比如我可以直接存放每个嘉宾的心仪位置
然后通过endflag=max(m[i],endflag+1)的方式更新,这样就不用讨论那么多种情况了。