【题目来源】http://www.codeforces.com/contest/268/problem/E
【个人体会】总算知道这个题目的正解了。。。借助了2位大神的力量(其实事实是我根本没出脑力,只打了个程序)。。。
【题目大意】有N首歌,每首歌喜欢的概率为P[I],每首歌的时间为L[I]。一共有N!种排列方式来录制这些歌曲,对于每种排列方式,这个人开始听,遇到喜欢的歌曲,会接着听下一首歌曲;遇到不喜欢的歌曲,会把在这首歌之前的所有喜欢的歌听一遍,之后再回头来接着听下一首歌曲。最后求出数学期望总时间最长的那个时间。
【题目分析】1.对于I和J这2个位置,倘若这个人喜欢I,并且不喜欢J,那么I这首歌将会被再听一遍。发生这件事情的概率是P[I]*(1-P[J]),这件事情将会产生L[I]*P[I]*(1-P[J])的数学期望时间加成。
2.假设现在只有2个人I和J,那么若L[I]*P[I]*(1-P[J]) > L[J]*P[J]*(1-P[I]),则说明I带来的数学期望时间加成会更多,那么I应当放在开头,J放在末尾。
3.对于人多的情况先考虑简单一点的,2个人相邻I和I+1,现在考虑是否要交换它们2个,
如果I在前面那么,数学期望的加成是:
(1-P[I])*(P[1]*L[1]+P[2]*L[2]+...+P[I-1]*L[I-1]) + (1-P[I+1])*(P[1]*L[1]+P[2]*L[2]+...+P[I-1]*L[I-1]+P[I]*L[I]);
如果I+1在前面,那么,数学期望的加成是
(1-p[I+1])*(P[1]*L[1]+P[2]*L[2]+...+P[I-1]*L[I-1]) + (1-P[I])*(P[1]*L[1]+P[2]*L[2]+...+P[I-1]*L[I-1]+P[I+1]*L[I+1]);
两式相减,最终取决于(1-P[I+1])*P[I]*L[I]和(1-P[I])*P[I+1]*L[I+1]的大小关系。
4.(1-P[I+1])*P[I]*L[I]和(1-P[I])*P[I+1]*L[I+1] 等价于 P[I]*L[I]/(1-P[I])和P[I+1]*L[I+1]/(1-P[I+1])
5.将P[I]*L[I]/(1-P[I])视为A[i],即I的比较函数。对于一个序列来说,倘若它有最大的数学期望值,那么A[i]必然是严格递减的,否则经过两两交换的方法,必然能使得总期望值最大。
【代码如下】
1 #include <iostream> 2 #include <iomanip> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <cmath> 7 #include <vector> 8 #include <deque> 9 #include <stack> 10 #include <queue> 11 #include <algorithm> 12 13 //#define FILE_IO 14 #define DEBUG 15 16 const int Maxn = 50005; 17 int N, A[Maxn]; 18 double L[Maxn], P[Maxn], Sum[Maxn], Ans; 19 20 using namespace std; 21 22 bool Cmp(const int &i, const int &j) 23 { 24 return (P[i] * L[i] * (1 - P[j] / 100)) > (P[j] * L[j] * (1 - P[i] / 100)); 25 } 26 27 int main() 28 { 29 #ifdef FILE_IO 30 freopen("test.in", "r", stdin); 31 #endif // FILE_IO 32 scanf("%d", &N); 33 for (int i = 1; i <= N; ++i) 34 { 35 scanf("%lf%lf", &L[i], &P[i]); 36 A[i] = i; 37 } 38 sort(A + 1, A + 1 + N, Cmp); 39 for (int i = 1; i <= N; ++i) 40 Sum[i] = Sum[i - 1] + L[A[i]] * P[A[i]]; 41 for (int i = 1; i <= N; ++i) 42 Ans += L[i] * 10000; 43 for (int i = 2; i <= N; ++i) 44 Ans += (100 - P[A[i]]) * Sum[i - 1]; 45 cout << setprecision(12) << setiosflags(ios :: fixed) << Ans / 10000 << endl; 46 return 0; 47 }