原题地址:http://codeforces.com/problemset/problem/383/A
题目大意:有 n 头奶牛,全部看着左边或者右边,现在开始给奶牛挤奶,给一头奶牛挤奶时,所有能看到它的奶牛会损失一升奶,已经挤过的奶牛不会再损失,求一种挤奶顺序,使得失去的牛奶数最少。(1 <= n <= 200000)
题目分析:考场上看到它的时候想:这不就是一个裸的线段树么……但是由于线段树不熟就没写出来……考完参照网上一些解法发现自己真的是想多了……唉……原来是贪心
我们需要证明以下一个性质:
最优值一定会出现在先挤完所有向左看的奶牛或者所有向右看的奶牛这两种情况之中(当然不止会出现在这两种情况之中),并且,我们一定先从最右边的向左看的奶牛或者最左边的向右看的奶牛挤起。
首先第二条性质不难理解,如果我们先从中间的一个挤起,必然不会比挤最边上的那头更优,可以用反证法来证明一下。
设suml[i]为从1 ~ i向左看的奶牛的个数,sumr[i]为从1 ~ i向右看的奶牛的个数,假设第k、j头奶牛是向左看的(且j在k的右边),若先挤第k头奶牛更优,则必须满足
suml[n] - suml[k] + sumr[k - 1] + suml[n] - suml[j] + sumr[j - 1] <= suml[n] - suml[j] + sumr[j - 1] + (suml[n] - 1 - suml[k]) + sumr[k - 1] (减一是因为 j 已经被挤过了,不可能再失去牛奶)这显然不会成立
第一条性质也因此变得显然了,加入我们先挤看着右边的,假设共有w头奶牛能看到最右边的看着左边的奶牛(权且成为P),那么同理,P能看到的看着右边的奶牛有且仅有w头,那么在挤这w头奶牛的过程中,P会失去w单位的奶,而如果我们先挤P,那w头奶牛也会失去w单位的奶,所以它们是等价的。
那么只需要扫一遍就好啦~
1 //date 20140121 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 6 using namespace std; 7 8 const int maxn = 200005; 9 10 inline int getint() 11 { 12 int ans(0); char w = getchar(); 13 while(‘0‘ > w || w > ‘9‘)w = getchar(); 14 while(‘0‘ <= w && w <= ‘9‘) 15 { 16 ans = ans * 10 + w - ‘0‘; 17 w = getchar(); 18 } 19 return ans; 20 } 21 22 int n; 23 int stu[maxn], l[maxn], r[maxn]; 24 25 int main() 26 { 27 // freopen("c.in", "r", stdin); 28 29 n = getint(); 30 for(int i = 1; i <= n; ++i)stu[i] = getint(); 31 for(int i = 2; i <= n; ++i)l[i] = l[i - 1] + stu[i - 1]; 32 for(int i = n - 1; i >= 1; --i)r[i] = r[i + 1] + (!stu[i + 1]); 33 long long a1 = 0, a2 = 0; 34 35 for(int i = n; i >= 1; --i)a1 += (long long)(stu[i] == 0 ? l[i] : 0); 36 for(int i = 1; i <= n; ++i)a2 += (long long)(stu[i] == 1 ? r[i] : 0); 37 38 cout << (a1 < a2 ? a1 : a2) << endl; 39 return 0; 40 }