zoukankan      html  css  js  c++  java
  • CF 1278C Berry Jam 题解

    Forewords

    说实话我是被图吸引进来的23333,图画的真的挺好看。

    题意

    你从一个梯子下到地下室,梯子左右两边各有 (n) 瓶果酱排成一排,果酱口味为草莓味或蓝莓味,你每次只能吃你左边或右边没吃过的那瓶,求最少要吃多少瓶才能使两种口味的果酱数量相等。

    题解

    以样例为例,有 (7) 罐草莓味的和 (5) 罐蓝莓味的。
    img1
    然后一直往右边吃,吃到这个时候,吃掉了 (3) 草蓝莓味的和 (2) 罐蓝莓味的,剩下 (r_ ext{left}=4) 罐草莓味的和 (b_ ext{left}=3) 罐蓝莓味的。
    img2
    这时候,为了达到目的,可以再吃 (r) 罐草莓味的和 (b) 罐蓝莓味的,可行方案如下表:

    (r) (b)
    (1) (0)
    (2) (1)
    (3) (2)
    (dots) (dots)

    可以发现,(r-b) 保持不变,且等于 (r_ ext{left}-b_ ext{left})

    这一结论很显然,因为 (r)(b) 只能减,不能增,因此达到目标的最好方法就是把数量多的口味吃到和另一个口味数量相等。然而这个方案并不一定能够达到,因此在这个的基础上再两种口味同时多吃 (v) 瓶。

    具体实现就是预处理单吃一侧不同数量时产生的差值,然后枚举吃另一侧 (1dots n) 瓶时要在另一侧吃的差值对应去找即可。

    注意处理一下边界。

    Code

    这里因为带了个 map 所以复杂度是 (O(nlog n)),但是 map 可以优化成数组,降到 (O(n))(比官方给的题解少一个 (log))。

    运用了一些小技巧来对调两边(其实可能不需要)。

    #include<cstdio>
    #include<map>
    const int MAXN=1e5+5;
    int a[MAXN],b[MAXN],t,bflag[MAXN],n,gtotr,gtotb,ans;
    std::map<int,int>map;
    int main()
    {
    	scanf("%d",&t);
    	t*=2;
    	int cur=0;
    	for(;t>=1;t--)
    	{
    		map.clear();gtotr=gtotb=0;
    		if((++cur)%2)
    		{
    			ans=2147483647;
    			scanf("%d",&n);
    			for(int i=1;i<=n;i++)
    				scanf("%d",&a[i]);
    			for(int i=1;i<=n;i++)
    				scanf("%d",&b[i]);
    		}
    		else
    		{
    			for(int i=1;i<=n;i++)
    				std::swap(a[i],b[n-i+1]);
    			/*
    			for(int i=1;i<=n;i++)
    				printf("%d ",a[i]);
    			for(int i=1;i<=n;i++)
    				printf("%d ",b[i]);
    			printf("
    ");
    			*/
    		}
    		for(int i=1;i<=n;i++)
    		{
    			if(a[i]==1) gtotr++; else gtotb++;
    		}
    		int totr=0,totb=0;
    		map[0]=-1;
    		for(int i=1;i<=n;i++)
    		{
    			if(b[i]==1) totr++; else totb++;
    			bflag[i]=totr-totb;
    			if(!map[bflag[i]]) map[bflag[i]]=i;
    //			printf("%d %d
    ",bflag[i],i);
    		}
    		gtotr+=totr;gtotb+=totb;
    		if(gtotr==gtotb) {if(!(cur%2))printf("0
    ");continue;}
    		totr=totb=0;
    		for(int i=n;i>=1;i--)
    		{
    			if(a[i]==1) totr++; else totb++;
    			int result=map[(gtotr-totr)-(gtotb-totb)];
    //			printf("Eat [%d %d], Left[%d %d], Result %d
    ",totr,totb,gtotr-totr,gtotb-totb,(gtotr-totr)-(gtotb-totb));
    			if(result) {if(result<0) result=0;ans=std::min(ans,n-i+1+result);}
    		}
    		if(!(cur%2)) printf("%d
    ",ans);
    	}
    	return 0;
    }
    
    无特别声明的情况下,本文为原创文章,允许转载,采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
    在声明禁止转载的情况下,请勿转载;若本文章为转载的文章,版权归原作者所有。
    如果您觉得本文写得好,请点击下方的推荐按钮~若您有任何建议和指正,请在下方留言,对于您的指正将不胜感激。
  • 相关阅读:
    定位属性position,相对定位,绝对定位
    flex弹性盒模型?
    vue生命周期
    理解cookie、session、token
    前端兼容性问题
    JS 如何为一个元素怎么绑定多个事件?
    js数组的操作方法
    vue页面翻页勾选的记忆功能
    Vue中nextTick的正确使用
    Vue用router.push(传参)跳转页面,参数改变,跳转页面数据不刷新的解决办法
  • 原文地址:https://www.cnblogs.com/ksyx/p/coderforces-1278C-solution.html
Copyright © 2011-2022 走看看