题目链接:http://codeforces.com/contest/912/problem/D
题目大意:
在一个\(n \times m\)的网格中放鱼(每个网格只能放一条鱼),用一个\(r \times r\)的网随机地捕一次鱼,问如何放置鱼能使得捕到的鱼的期望值最大,求最大值。
知识点: 优先队列、概率与期望
解题思路:
要使捕到鱼的期望值最大,就应该往最有可能被渔网捞到的格子里放鱼。
\(P(某个格子被捞到)= N_1(渔网能捞到这个格子的放置方案数)/N_2(渔网总放置方案数),\)
\(N(渔网总放置方案数) = (n-r+1)\times(m-r+1)\)
而要求\(N_1\),我们可以先算出最左边一列和最上边一行的各个格子对应的\(N_1\),\(N_1(x,y) = N_1(x,0) \times N_1(0,y)\)。于是我们可以从最中间的格子开始(直觉告诉我们:最中间的格子总是最有可能被捞到),用一个优先队列维护,每次都往上下左右四个方向拓展,找出前 \(k\) 个最有可能被捞到的位置所对应的期望值,加起来所得到的总和即为答案。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 const int maxn = 1e5+5; 5 const int cx[4]={1,-1,0,0},cy[4]={0,0,1,-1}; 6 7 int line[maxn],col[maxn]; 8 struct node{ 9 int x,y; 10 double z; 11 friend bool operator <(const node &a,const node &b){ 12 return a.z<b.z; 13 } 14 }; 15 map<pair<int,int>,int> vis; 16 priority_queue<node> q; 17 18 int main(){ 19 int n,m,r,k; 20 scanf("%d%d%d%d",&n,&m,&r,&k); 21 for(int i=1;i<=(n+1)/2;i++){ 22 if(i<=n-r+1) line[i]=min(i,r); 23 else line[i]=line[i-1]; 24 } 25 for(int i=n,j=1;i>(n+1)/2;i--,j++) 26 line[i]=line[j]; 27 28 for(int i=1;i<=(m+1)/2;i++){ 29 if(i<=m-r+1) col[i]=min(i,r); 30 else col[i]=col[i-1]; 31 } 32 for(int i=m,j=1;i>(m+1)/2;i--,j++) 33 col[i]=col[j]; 34 35 double tot=(double)(n-r+1)*(m-r+1),ans=0.0; 36 node now,next; 37 now.x=(n+1)/2,now.y=(m+1)/2; 38 now.z=(double)line[now.x]*col[now.y]/tot; 39 q.push(now); 40 vis[make_pair(now.x,now.y)]=1; 41 while(k--){ 42 now=q.top(); 43 q.pop(); 44 ans+=now.z; 45 for(int i=0;i<4;i++){ 46 int nx=now.x+cx[i],ny=now.y+cy[i]; 47 if(nx>0&&nx<=n&&ny>0&&ny<=m&&!vis[make_pair(nx,ny)]){ 48 next.x=nx,next.y=ny,next.z=(double)line[next.x]*col[next.y]/tot; 49 q.push(next); 50 vis[make_pair(nx,ny)]=1; 51 } 52 } 53 } 54 printf("%.10lf\n",ans); 55 56 return 0; 57 }