zoukankan      html  css  js  c++  java
  • 4.23 子集 分数规划 二分 贪心 set 单峰函数 三分

    avatar
    avatar

    思维题。

    显然考虑爆搜。然后考虑n^2能做不能。

    容易想到枚举中间的数字mid 然后往mid两边加数字 使其整个集合权值最大。

    这里有一个比较显然的贪心就不再赘述了。

    可以发现这样做对于集合是奇数的时候可以遍历到所有最优的情况。这一步复杂度为n^2.

    但是值得注意的是 如果集合为偶数的时候怎么解决 暴力枚举两个数字在中间这复杂度已经是n^3的了 再向两边拓展复杂度会更高。

    有多种解决方法:1 尝试证明偶数的序列一定没有奇数的优。2 尝试证明两个数字只有是相邻的时候比其他不相邻的更优 3 使用固定左端点 移动右端点的方法 这样可以做到n^2.

    使用方法3过于无脑 我考试的时候 脑子不太好使没想到1这个性质 只是把2证明了一下。还是很容易证明的。

    至此得到了一个n^2的做法。

    考虑正解。经过不断的推式子 可以发现这类似于分数规划问题。可以先二分答案。

    然后就是对于每一个中点直接求出最大的序列的值是否满足二分的答案即可。

    可以发现这样做是n^2logn的。

    不过考虑一个端点不断的向右移动可以发现每次最多加入两个数字 所以利用这个东西可以扫一遍得到答案。

    不过考试sb了 外面套了一个set简化代码 其实是把代码和时间复杂度都复杂化了 直接记录两个端点就行了。

    对于偶数也是如此。时间复杂度nlog.(不过考试的时候套了一个set复杂度nlog^2.

    对于题解:容易证明奇数比偶数更优 自证不难。

    然后考虑枚举中位数。显然选取的个数与价值是严格单峰的所以三分一下就行了。

    code:set代码。

    const int MAXN=200010;
    int n,top,maxx;db ans;
    int a[MAXN],b[MAXN],vis[MAXN];
    multiset<int>s1,s2;
    multiset<int>::iterator it1,it2;
    inline void dfs(int x)
    {
    	if(x==n+1)
    	{
    		top=0;db cnt=0;
    		rep(1,n,i)if(vis[i])b[++top]=a[i],cnt+=a[i];
    		cnt=cnt/top;
    		if(top&1)cnt-=b[(top>>1)+1];
    		else cnt-=(1.0*b[(top>>1)]+1.0*b[(top>>1)+1])/2;
    		ans=max(ans,cnt);
    		return;
    	}
    	vis[x]=1;
    	dfs(x+1);
    	vis[x]=0;
    	dfs(x+1);
    }
    inline int check(db x)
    {
    	db sum1=0,sum2=x;
    	int L=0,R=n;ll cnt=0;
    	s1.clear();s2.clear();
    	rep(2,n,i)//处理单个中位数.
    	{
    		L=L==i-2?i-1:L;
    		if(s1.size())//更改
    		{
    			it1=s1.begin();
    			sum1-=*it1;
    			s1.erase(it1);
    			sum1+=a[i-1];
    			s1.insert(a[i-1]);
    			++L;
    		}
    		if(a[L]+a[R]-x-x-a[i]-a[i]>=0&&R>i&&L)//插入
    		{
    			s1.insert(a[L]);sum2+=x+x;
    			s2.insert(a[R]);
    			sum1+=a[L];sum1+=a[R];cnt+=2;
    			--R;--L;
    		}
    		if(R+1==i)
    		{
    			it1=s1.begin();
    			sum1-=*it1;
    			it2=s2.begin();
    			sum1-=*it2;
    			s1.erase(it1);
    			s2.erase(it2);
    			++R;++L;cnt-=2;
    			sum2=sum2-x-x;
    		}
    		while(s1.size())
    		{
    			it1=s1.begin();
    			it2=s2.begin();
    			if(*it1+*it2-x-x-a[i]-a[i]<0)
    			{
    				sum1-=*it1;
    				sum1-=*it2;cnt-=2;
    				s1.erase(it1);
    				s2.erase(it2);
    				sum2=sum2-x-x;
    				++R;++L;
    			}
    			else break;
    		}
    		if(sum1-sum2-cnt*a[i]>=0)return 1;
    	}
    	return 0;
    }
    int main()
    {
    	freopen("subset.in","r",stdin);
    	freopen("subset.out","w",stdout);
    	get(n);
    	rep(1,n,i)get(a[i]),maxx=max(maxx,a[i]);
    	sort(a+1,a+1+n);
    	if(n<=20)
    	{
    		dfs(1);
    		printf("%.5lf
    ",ans);
    		return 0;
    		ans=0;
    	}
    	if(n<=2000)
    	{
    		rep(1,n,i)//枚举中位数的位置
    		{
    			db sum=0;int cnt=1;
    			for(int j=1;j<=min(i-1,n-i);++j)
    			{
    				cnt+=2;
    				sum+=a[i-j]+a[n-j+1]-a[i]-a[i];
    				ans=max(ans,sum/cnt);
    			}
    		}
    		printf("%.5lf
    ",ans);
    		return 0;
    	}
    	db l=0,r=maxx;
    	while(l+EPS<r)
    	{
    		db mid=(l+r)/2;
    		if(check(mid))l=mid;
    		else r=mid;
    	}
    	printf("%.5lf
    ",r);
    	return 0;
    }
    
  • 相关阅读:
    Mysql安装
    mysql 密码过期
    svn 合并分支
    idea 分支主干管理
    linux删除数据恢复,extundelete
    linux 转换文件编码
    sina 接口 根据ip获取各个国家和地区
    SQL中char、varchar、nvarchar的区别
    C#中virtual和abstract区别,举例子
    父类和子类的关系、代码例子
  • 原文地址:https://www.cnblogs.com/chdy/p/12762953.html
Copyright © 2011-2022 走看看