zoukankan      html  css  js  c++  java
  • Codeforces 1239E. Turtle 折半

    原文链接www.cnblogs.com/zhouzhendong/p/CF1239E.html

    前言

    咕了这么久之后,我的博客复活了!

    题解

    结论1

    存在一个最优解(A)数组,满足(forall 1leq i <n, A_{1,i}leq A_{1,i+1}, A_{2,i}geq A_{2,i+1})

    证明提示:假设 (A_{1,i} > A_{1,j},i<j),那么,交换这两个值之后得到的答案严格不劣,另一行证法相同。

    结论2

    (A)数组满足(forall 1leq i <n, A_{1,i}leq A_{1,i+1}, A_{2,i}geq A_{2,i+1})时,从左上角走到右下角存在一种方案,使得经过的权值之和最大,并且经过整个第一行或整个第二行。

    证明

    定义路径 (i) 表示经过 (A_{1,i})(A_{2,i}) 的合法路径。那么可以证明,当 (1<i<n) 时,路径 (i-1) 和路径 (i+1) 中至少存在一个不劣于路径 (i)

    具体地,记 (s_i) 表示路径 (i) 的权值和,那么

    [s_{i+1} - s_i = A_{1,i+1} - A_{2,i}\ s_{i-1}-s_i = A_{2,i-1} - A_{1,i} ]

    假设 (s_i>s_{i-1},s_i>s_{i+1}) ,那么

    [A_{1,i+1} < A_{2,i} , A_{2,i-1}<A_{1,i} ]

    又因为

    [A_{1,i}leq A_{1,i+1}, A_{2,i}leq A_{2,i-1} ]

    所以

    [A_{1,i}leq A_{1,i+1}<A_{2,i}leq A_{2,i-1}<A_{1,i}Longrightarrow A_{1,i}<A_{1,i} ]

    矛盾,故 (s_ileq s_{i-1},s_ileq s_{i+1}) 二者至少有一个正确。

    假设 (s_ileq s_{i-1}) 成立,那么 (A_{1,i}leq A_{2,i-1}) ,所以 (A_{1,i-1}leq A_{1,i}leq A_{2,i-1}leq A_{2,i-2}),所以可以得到 (s_{i-1}leq s_{i-2}),即

    [s_ileq s_{i-1} Rightarrow s_{i-1}leq s_{i-2} ]

    于是可得 (s_ileq s_1)

    (s_ileq s_{i+1}) ,则同理可得 (s_ileq s_n)

    综上所述,(s_ileq s_1,s_ileq s_n) 二者至少有一个正确。

    所以,对于 (1<i<n) ,必然有 (s_1) 或者 (s_n) 不劣于 (s_i),所以,(max(s_1,s_n) = max_{i = 1}^{n} (s_i))

    于是结论2得证。

    解决问题

    我们现在要解决的问题变成:将最小值和次小值分别置于左上角和右下角,其余的数分成权值和尽量平均的两份,也就是最小化权值和的两部分 (max)

    由于输入的 (a) 数组值域较小,容易想到使用背包算法解决剩余的问题,并使用 bitset 优化,但是考虑到要记录方案,无法使用 bitset

    考虑数的个数不多,我们可以采用折半的方法,每一半有 (n-1) 个数。对于每一半,分别暴力枚举每一个数是否被选,把所有方案状态压缩之后记到二维数组中。数组的两维分别表示选了几个数、这些数的和。

    最后,再使用双指针扫描数组获取最优解即可。这部分可以参照代码理解。

    代码

    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof x)
    #define For(i,a,b) for (int i=(a);i<=(b);i++)
    #define Fod(i,b,a) for (int i=(b);i>=(a);i--)
    #define pb(x) push_back(x)
    #define mp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    #define outval(x) cerr<<#x" = "<<x<<endl
    #define outtag(x) cerr<<"-----------------"#x"-----------------
    "
    #define outarr(a,L,R) cerr<<#a"["<<L<<".."<<R<<"] = ";
                        For(_x,L,R) cerr<<a[_x]<<" ";cerr<<endl;
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair <int,int> pii;
    LL read(){
        LL x=0,f=0;
        char ch=getchar();
        while (!isdigit(ch))
            f=ch=='-',ch=getchar();
        while (isdigit(ch))
            x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return f?-x:x;
    }
    const int N=26,S=N*50000;
    int n,s;
    vector <int> a,b;
    bitset <S> va[N],vb[N];
    int sta[N][S],stb[N][S];
    void SolveA(int k,int cnt,int sum,int st){
    	if (k==n-1){
    		if (!va[cnt][sum]){
    			va[cnt][sum]=1;
    			sta[cnt][sum]=st;
    		}
    		return;
    	}
    	SolveA(k+1,cnt,sum,st);
    	SolveA(k+1,cnt+1,sum+a[k],st|1<<k);
    }
    void SolveB(int k,int cnt,int sum,int st){
    	if (k==n-1){
    		if (!vb[cnt][sum]){
    			vb[cnt][sum]=1;
    			stb[cnt][sum]=st;
    		}
    		return;
    	}
    	SolveB(k+1,cnt,sum,st);
    	SolveB(k+1,cnt+1,sum+b[k],st|1<<k);
    }
    int ans=1e9,ansa,ansb;
    int res[2][N];
    void chkans(int i,int j,int ap,int bp){
    	int val=max(ap+bp,s-ap-bp);
    	if (val<ans){
    		ans=val;
    		ansa=sta[i][ap];
    		ansb=stb[j][bp];
    	}
    }
    int main(){
    	n=read();
    	For(i,1,n*2)
    		a.pb(read());
    	sort(a.begin(),a.end());
    	reverse(a.begin(),a.end());
    	res[0][1]=a.back(),a.pop_back();
    	res[1][n]=a.back(),a.pop_back();
    	s=0;
    	for (auto i : a)
    		s+=i;
    	For(i,1,n-1)
    		b.pb(a.back()),a.pop_back();
    	SolveA(0,0,0,0);
    	SolveB(0,0,0,0);
    	For(i,0,n-1){
    		int j=n-1-i;
    		vector <int> bp;
    		For(k,0,s)
    			if (vb[j][k])
    				bp.pb(k);
    		For(k,0,s){
    			if (!va[i][k])
    				continue;
    			while (bp.size()>=2&&(bp[bp.size()-2]+k)*2>=s)
    				bp.pop_back();
    			if (bp.size()>0)
    				chkans(i,j,k,bp.back());
    			if (bp.size()>=2)
    				chkans(i,j,k,bp[bp.size()-2]);
    		}
    	}
    	int t0=1,t1=0;
    	For(i,0,n-2)
    		if (ansa>>i&1)
    			res[0][++t0]=a[i];
    		else
    			res[1][++t1]=a[i];
    	For(i,0,n-2)
    		if (ansb>>i&1)
    			res[0][++t0]=b[i];
    		else
    			res[1][++t1]=b[i];
    	sort(res[0]+2,res[0]+n+1);
    	sort(res[1]+1,res[1]+n);
    	reverse(res[1]+1,res[1]+n);
    	For(i,0,1){
    		For(j,1,n)
    			printf("%d ",res[i][j]);
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    简单的JAVAWeb选课系统
    公文流转系统001
    第九周动手动脑
    JAVA文件操作
    动手动脑-异常处理
    个人NABCD
    水王(课堂作业)
    软件学习进度表07
    软件工程学习进度表06
    软件工程个人作业05(二维数组求最大子数组的和)
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1239E.html
Copyright © 2011-2022 走看看