zoukankan      html  css  js  c++  java
  • Codeforces 1204D2. Kirk and a Binary String (hard version) (dp思路)

    题目链接:http://codeforces.com/contest/1204/problem/D2

    题目是给定一个01字符串,让你尽可能多地改变1变为0,但是要保证新的字符串,对任意的L,R使得Sl,Sl+1,Sl+2...Sr的最长不递减子序列长度保持不变,求新的串s。

    dp思路,从前往后遍历串s。

    1 . 遇到s[ i ] = 0 是不能改变的,因为从i到n的最长不递减子序列必定是以s[ i ] = 0为起点的,改变之后会减少长度。

    2 . 遇到s[ i ] = 1。我们考虑如果变为0,首先从0到 i-1 的最长不递减子序列是不受到影响的,因为从0到 i 的最长不递减子序列必定是以s [ i ] = 1为结尾的,不影响0到i -1 的最长不递减子序列,那么在0到i区间内, 我们需要保证条件1:任意的x(0<x<i)到 i 最长不递减子序列不发生改变,这里只要保证0到 i 的最长不递减子序列不变即可,任意的x(0<x<i)到 i 的最长不递减子序列就不会改变,因为0到x不会因为s[ i ]的变化受到影响,所以只要0到 i 的最长不递减子序列不发生改变,则x到 i 也不会改变。如果此时将s[ i ] 从1变成 0,要要0到 i 的最长不递减子序列不变,只有是以s[ i ] = 0为结尾的最长不递减子序列(原因上述有提到)的长度不变才可以,以s[ i ] = 0为结尾只能是一组0000...00,中间不能穿插1,所以其等价条件就是zeros + 1 == LIS1[ i ] (zeros表示从0到i中0的个数,LIS1表示0到i的最长不递减子序列),这里便满足条件1了。

           需要满足的条件2:既然0到 i 已经保证,那么 i 到 n也是同样的道理,s[ i ] 由1变为0,此时 i 到n的最长不递减子序列势必是以s[ i ] = 0 为起点,其长度可以表示为LIS2[ i + 1 ]+1    // ( LIS2[ i +1 ] 表示i + 1 到n的最长不递减子序列) //。如果改变之前的最长不递减子序列是以s [ i ] = 1为起点,改变之后仍然以s[ i ] 为起点的话就没有问题,如果改变之前不是以S[ i ]为起点,改变之后从i到的n的最长不递减子序列必定会增加,因为i到n最长不递减子序列不管是以0为起点还是1为起点,当s[ i ] = 0时,肯定会与其拼接上,长度会+1。以上的等价条件就转化为 LIS2[ i ] == LIS2[ i +1 ] +1

    满足以上两个条件的情况下,就可以使s[ i ] = 1变为0。

    PS:在求解最长不递减子序列的过程中,如果用O(n^2)的算法对hard version会超时,easy version可以过。所以只能选择nlogn复杂度的算法。但是对于求LIS2[ i ] (i到n的最长不递减子序列),我没有想到相对简单的办法,因为不会lower_bound()的重载,所以只能选择较为麻烦的方法(具体看代码), 如果哪位朋友有更简便的做法可以留言一起交流一下。

    AC代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define maxn 100005
    using namespace std;
    int LIS1[maxn],LIS2[maxn],ones[maxn];
    char low1[maxn],low2[maxn];//low[i]数组记录长度为i的最长不递减子序列结尾最小的元素 
    string s;
    int zeros = 0,ans = 1; 
    int main(){
    	cin>>s;
    	LIS1[0] = 1,low1[1] = s[0];
    	for(int i = 1;i<s.length();i++){
    		if(s[i] >= low1[ans]){
    			low1[++ans] = s[i];
    		}
    		else{
    			int k = upper_bound(low1,low1 + ans + 1,s[i]) - low1 ;
    			low1 [k] = s[i];
    		}
    		LIS1[i] = ans;
    	}//用nlogn时间复杂度求从左到右求最长上升子序列 
    	
    	int cnt_one = 0;//从右到左记录1的个数 
    	if(s[s.length() -1] == '1'){
    		cnt_one++;
    	}
    	ans = 1,LIS2[s.length() -1] = 1,low2[ans] = s[s.length()-1];
    	for(int i = s.length() -2;i>=0;i--){ 
    		if(s[i]<= low2[ans]){
    			low2[++ans] = s[i];//low2[++ans]中++ans长度的最长不递减子序列起始值改为s[i] 
    		}
    		if(s[i] == '1'){
    			cnt_one++;//ans1记录1的个数 
    		}
    		if(ans == cnt_one){
    			low2[ans] = '1';//贪心策略,如果此时ans和 cnt相等,
    			                //把长度为 ans 的从左到右最长不递减子序列的起始值改为1,方便后续可以拼接0 
    		}
    		LIS2[i] = ans;
    	}//从右到左,求i到s.length()-1的最长不递减子序列 
    	for(int i = 0;i<s.length() ;i++){
    		if(s[i] == '0'){
    			zeros++;//前i个元素中0的个数 
    		}
    		if(s[i] == '1'){
    			if(zeros + 1 == LIS1[i] && LIS2[i] == LIS2[i+1] + 1){
    				s[i] = '0';//如果两者相等,则可以把1改为0 
    				zeros++;
    			}
    		}
    	}
    	cout<<s;
    	return 0;
    } 
  • 相关阅读:
    2011Android技术面试整理附有详细答案(包括百度、新浪、中科软等多家公司笔试面试题)
    SQL注入攻击与防御
    从零开始学习jQuery
    linux内核定时器
    国内外 Java Script 经典封装
    jQuery EasyUI API 中文文档
    新手该怎么学习DIV+CSS网页标准布局?
    3种方法修改PHP时区
    linq教程
    BizTalk Server 系列文章目录
  • 原文地址:https://www.cnblogs.com/AaronChang/p/12129637.html
Copyright © 2011-2022 走看看