原题链接:Leha and Function
要点:贪心,外加排列组合
题意:函数F(n,k)是啥?从1~n的集合中选择k个数字构成一个子集,所有子集的最小值的贡献的平均值就是F(n,k)。
For example, let’s find F(4, 2). All possible 2-element subsets of {1,2,3,4} are: {1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}. Their minimal values are 1, 1, 1, 2, 2, 3. So the average (expected) value is F(4,2)=(1+1+1+2+2+3)/6=10/6=1.66666666
但这还不够难,再给你A,B两个元素个数相等的集合,保证A中任意元素>=B中任意元素,要求你重新排列A中的元素得到集合
A
1
A^1
A1,使得
∑
i
=
1
m
F
(
A
i
1
,
B
i
)
\sum_{i=1}^mF(A_i^1,B_i)
∑i=1mF(Ai1,Bi)取最大值。
感性分析:对于F(n,k),当n不变,k越小,结果越大;当k不变,n越大,结果越大。
进一步分析:因为n从A里拿,k从B里拿,所以套用贪心的思路,A的最大元素对应B的最小元素,A的次大元素对应B的次小元素,以此类推.
#include <cstdio> //更快的输入
#include <algorithm>
using namespace std;
const int MAXN=2e5+10;
struct node{
int d,no;//d为node的数据,no为node的编号
}b[MAXN];
int a[MAXN],c[MAXN];
int main(){
int m;
scanf("%d",&m);
for(int i=0;i<m;i++) scanf("%d",&a[i]);
for(int i=0;i<m;b[i].no=i,i++) scanf("%d",&b[i].d);//输入时候顺便编号
sort(a,a+m);//从小到大排
sort(b,b+m,[](node x,node y){return x.d>y.d;});//从大到小排,用lambda表达式紧凑一点
for(int i=0;i<m;i++) c[b[i].no]=a[i];//根据编号归位A中元素
for(int i=0;i<m;i++) printf("%d ",c[i]);
return 0;
}
理性分析:我数学分析只考了27分,所以请各位抱着批判的眼光来阅读…
已知n,k,求证当k不变,n越大,F(n,k)越大
对于所有符合条件的子集,其中最小元素为1的集合有
C
k
−
1
n
−
1
C_{k-1}^{n-1}
Ck−1n−1个。(确定含有一个1,再从剩下n-1个数字里选k-1个)
最小元素为2的集合有
C
k
−
1
n
−
2
C_{k-1}^{n-2}
Ck−1n−2个。(确定含有一个2,再从除1以外的n-2数字里选k-1个)
最小元素为3的集合有
C
k
−
1
n
−
3
C_{k-1}^{n-3}
Ck−1n−3个。(确定含有一个3,再从除1和2以外的n-3数字里选k-1个)
…
最小元素为n-k+1的集合有
C
k
−
1
k
−
1
C_{k-1}^{k-1}
Ck−1k−1个。(只剩下最后k个元素组成一个集合)
即F(n,k)=
[
∑
i
=
1
k
−
1
(
i
∗
C
k
−
1
n
−
i
)
]
/
[
∑
i
=
1
k
−
1
(
C
k
−
1
n
−
i
)
]
[\sum_{i=1}^{k-1}{(i*C_{k-1}^{n-i})}]/[\sum_{i=1}^{k-1}{(C_{k-1}^{n-i})}]
[∑i=1k−1(i∗Ck−1n−i)]/[∑i=1k−1(Ck−1n−i)]
化简可得:F(n,k)=(n+1)/(k+1)
显然,当k不变,n越大,F(n,k)越大
总觉得哪里不对,但好像举不出什么反例······