zoukankan      html  css  js  c++  java
  • P2882 [USACO07MAR]Face The Right Way [贪心+模拟]

    题目描述

    N头牛排成一列1<=N<=5000。每头牛或者向前或者向后。为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向1<=K<=N,求操作的最少 次数M和对应的最小K。

    简单题意:给你一个01串,每次可以对长度为K的区间进行异或,求异或的最少次数以及对应的K。

    解析:

    这是一道有毒的模拟,我做了一个下午加加减减鼓捣半天都快吐(秃)了。

    看到题第一反应二分答案,不过仔细一想,K和M并不相关,答案不具备单调性,无法二分出解。

    所以,这道题其实是个模拟。

    一个显而易见的贪心:从左往右,遇到0就以它为起点的K个点做异或,显然是最优的,每个0都至少要被异或一次,如果线性异或的话可以保证每个0只被异或一次,否则一定不优。

    于是枚举K找答案。直接暴力模拟复杂度达到(O(n^3)),80pnts,无法接受。

    考虑一个类似差分的优化,对于某一为0的位置(i),假设当前使用的长度为(X),那么我们在这个0的位置(i)打个标记(A),再在位置(i+X)的位置打个标记(B),表示从(A)开始到(B)进行了一次异或。

    思考如何去打这个标记(卡我一下午),直接差分肯定是不行的。我们可以讨论任意一个位置(i),看它前面有多少个(A,B)标记,发现位置(i)的异或次数正是该位置前面(A)的标记数量-(B)的标记数量((A)的量一定大于(B),这点很好理解)。

    于是代码实现我又卡了(我好蒻)。

    换个思路。

    思考直接用异或打标记,方便又好理解,搞一个数组去记录标记(B)(A)标记我们可以直接在枚举时实现。

    考虑用一个类似指针的东西当作标记(A)(有点不同于前面的(A)),在数组种从左往右移动,初值为0,遇到不同的数就对1进行异或,表示从这个遇到的不同的数位置开始进行翻转(有点绕),直到遇到一个标记(B)再把它异或回去为止。值得注意的是,这里我们并不需要对原数组进行改变,实际上(A)标记已经帮我们实现了“对(A)(B)这个区间进行异或”的目的了(想一想,为什么)。这个确实有点难以理解,毕竟我搞了一下午呢

    如果用之前那种打标记法我觉得比较难理解,毕竟异或比较贴近原题,虽然而者本质相同。

    参考代码:

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define INF 0x3f3f3f3f
    #define N 5010
    using namespace std;
    int n,a[N],sum[N],d[N];
    int main()
    {
    	scanf("%d",&n);
    	char c;
    	for(int i=1;i<=n;++i){getchar();c=getchar();(c=='B')?a[i]=0:a[i]=1;}
    	int cnt=0,ans=INF,pos;
    	for(int k=1;k<=n;++k){
    		int flag=0;cnt=0;
    		bool can=1;
    		memset(d,0,sizeof(d));
    		for(int i=1;i<=n;i++){
    			flag^=d[i];
    			if(!a[i]^flag){
    				if(i+k-1>n){can=0;break;}
    				flag^=1,d[i+k]^=1;cnt++;
    			}
    		}
    		if(can&&ans>cnt) ans=cnt,pos=k; 
    	}
    	cout<<pos<<' '<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Linux下的iwpriv(iwlist、iwconfig)的简单应用
    OCX控件的注册卸载,以及判断是否注册
    .OCX、.dll文件注册命令Regsvr32的使用
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11256863.html
Copyright © 2011-2022 走看看