zoukankan      html  css  js  c++  java
  • LG5308 [COCI2019] Quiz(wqs二分+斜率优化DP)

    LG5308 [COCI2019] Quiz

    作为 wqs 二分的一道入门题,值得写一篇题解。

    解题思路

    首先我们考虑 (O(n^2k)) 的普通 DP。

    我们令 (f_{i,k})​ 为考虑淘汰 (i) 个人,分成 (k) 轮淘汰的最大收益。我们可以得到转移方程:

    [f_{i,k}=maxlimits_{j=0}^{i-1} f_{j,k-1}+frac{i-j}{i} ]

    这个式子很难优化。二维的式子一般都很难进行优化。我们考虑化为一维状态,显然不太好化。

    我们考虑先不管 (k)​ 的限制,那么我们有:

    [f_{i}=maxlimits_{j=0}^{i-1} f_{j}+frac{i-j}{i} ]

    这样我们对这个式子进行 DP。(显然不考虑 (k)(f_i=sumlimits_{i=1}^i frac 1i),但是我们先不管这个)

    对于 (k,j) 两个转移点,若 (j) 优于 (k),我们有:

    [f_{j}+frac{i-j}{i}>f_{k}+frac{i-k}{i} ]

    [frac{f_j-f_k}{j-k}>frac 1i ]

    显然可以进行斜率优化了。时间复杂度为 (O(n))

    我们在记录一个 (g_i),记录 (f_i) 这个状态分割了几段。显然,若最优转移点为 (j)(g_i=g_j+1)

    现在考虑对于每次分割给予一些“奖赏”(“惩罚”也彳亍)。假设奖赏为 (x,xin R),则转移方程变成了:

    [f_{i}=maxlimits_{j=0}^{i-1} f_{j}+frac{i-j}{i}+x ]

    显然 (x) 越小分的段数越少。读者自证不难

    而且这个方程还是可以斜率优化,式子和上面那个一样。但是此时我们可以通过控制 (x) 来控制分的段数。显然可以二分 (x)。每次检验 (g_n)(k) 的关系即可。

    二分出合适的 (x) 后把奖赏退回去即可。

    时间复杂度 (O(nlog N))

    代码实现

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define lb long double
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=1e5+10; 
    const lb eps=1e-11;
    
    int n,m,h,t;
    int q[MAXN],g[MAXN];
    lb f[MAXN];
    lb k(int x,int y) {
    	return (f[x]-f[y])/(x-y*1.0);
    }
    
    int dp(lb x) {
    	h=t=1;
    	q[1]=0;
    	for(int i=1;i<=n;i++) {
    		while(h<t&&k(q[h],q[h+1])>eps+1.00/i) {
    			h++;
    		}
    		f[i]=f[q[h]]+(i*1.0-1.0*q[h])/(1.0*i)+x;
    		g[i]=g[q[h]]+1;
    		while(h<t&&k(q[t],q[t-1])+eps<k(q[t],i)) {
    			t--;
    		}
    		q[++t]=i;
    	}
    	return g[n]>m;
    }
    
    signed main() {
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
    
    	cin>>n>>m;
    	
    	lb l=-1e9,r=1e9,mid=0;
    	for(int i=1;i<=150;i++) {
    		mid=(l+r)/2;
    		if(dp(mid)) {
    			r=mid;
    		}
    		else {
    			l=mid;
    		}
    	}
    	
    	dp((l+r)/2);
    	cout<<fixed<<setprecision(9)<<f[n]-mid*m<<endl;
    
    	//fclose(stdin);
    	//fclose(stdout);
    	return 0;
    }
    
    
    
  • 相关阅读:
    20199306 2019-2020-2 《网络攻防实践》第七周作业
    20199306 2019-2020-2 《网络攻防实践》第6周作业
    20199306 2019-2020-2 《网络攻防实践》第五周作业
    20199306 2019-2020-2 《网络攻防实践》第4周作业
    20199317 2019-2020-2 《网络攻防实践》综合实践
    20199317 2019-2020-2 《网络攻防实践》第11周作业
    20199317 2019-2020-2 《网络攻防实践》第10周作业
    20199317 2019-2020-2 《网络攻防实践》第9周作业
    20199317 2019-2020-2 《网络攻防实践》第8周作业
    20199317 2019-2020-2 《网络攻防实践》第7周作业
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/LG5308.html
Copyright © 2011-2022 走看看