http://acm.hdu.edu.cn/showproblem.php?pid=5089
给出N道难度递增的题目,难度用可能做出的百分比表示,选出K道题目使得做出K-1道题目的概率最大。
选k题的情况下做出k-1的概率为所有(1-p)*p*p...的和,直接尝试转化这个式子的效果并不明显。
换个思路,假设最优解已经包含了k-1个了,现在来选取最后一个。K-1个全部做出的概率是Pall(k−1),有一道为做出的概率是Pless(k−1),现在选取的是PCk,那么做出K-1道的概率是
Pall(k−1)∗(1−P[PCk])+Pless(k−1)∗P[PCk]=
Pall(k−1)+P[PCk]∗(Pless(k−1)−Pall(k−1))
这是一个关于PCk的一次函数,如果Pless(k−1)−Pall(k−1)为正,选取最大的PCk,否则选取最小的。
那么我们每次选取的时候一定是选择剩下的最大或最小,那么说明答案一定是选取两边的概率,枚举比较一下就可以算出最大的概率了。
但是要求的是字典序最小的。左边不用管,对于右边,如果存在相同的value,应该选取index较小的。然后随便搞下就ok了。
#include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <queue> #include <map> #include <iostream> #include <algorithm> using namespace std; #define RD(x) scanf("%d",&x) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define clr0(x) memset(x,0,sizeof(x)) #define clr1(x) memset(x,-1,sizeof(x)) #define eps 1e-9 const double pi = acos(-1.0); typedef long long LL; typedef unsigned long long ULL; const int modo = 1e9 + 7; const int INF = 0x3f3f3f3f; const int inf = 0x3fffffff; const LL _inf = 1e18; const int maxn = 55,maxm = 1<<12; int n,k; bool vis[maxn]; double p[maxn]; int pos[maxn],s[maxn]; int main() { int _,_g;RD(_); while(_--){ RD2(n,k); clr0(vis); for(int i = 0;i < n;++i){ RD(s[i]); p[i] = ((double)s[i])/100.0; } double mx = 0; for(int g = 0;g <= k;++g){//选择前g个和后k - g个 double _p,res = 0; int cnt = 0; for(int i = 0;i < g;++i) pos[cnt++] = i; for(int i = 1;i <= k - g;++i) pos[cnt++] = n - i; for(int i = 0;i < cnt;++i){ _p = 1; for(int j = 0;j < cnt;++j){ if(j != i) _p *= p[pos[j]]; else _p *= (1 - p[pos[j]]); } res += _p; } if(mx - res < eps){ mx = res; _g = g; } } for(int i = 0;i < _g;++i) vis[i] = 1; for(int i = n - (k - _g);i < n;++i){ for(int j = i;j >= 0;--j){ if(vis[j] || s[j] > s[i]) break; _g = j; } vis[_g] = 1; } _g = 1; for(int i = 0;i < n;++i) if(vis[i]) printf("%d%c",i+1," \n"[_g == k]),_g++; } return 0; }