Codeforces Round #642 (Div. 3)

10 篇文章 0 订阅
3 篇文章 0 订阅

22分钟A了前三题,然后…然后就没然后了,这里补下DE题

传送门

D题解题思路:

  • 这个题开始就是找规律…找…
  • 正解是set或者优先队列,这里用优先队列写下
  • 首先我们是从哪个区间大我们就往哪个区间的中间位置放数,所以我们就存储他的头和尾的坐标,一开始肯定是1,n
  • 我们这里对优先队列排序,按照长度由长到短,如果长度相同那么看坐标小就排在前面(这里学了下优先队列的排序,与sort是反的)
struct cmp{
    bool operator()(PII x, PII y){
        if (x.second - x.first == y.second - y.first){
            return x.first > y.first;
        }
        else{
            return x.second - x.first < y.second - y.first;
        }
    }
};

priority_queue<PII,vector<PII>,cmp> q;
  • 然后我们对这个区间进行分解,按照中间位置分为左右2部分,然后首尾坐标push进队列
  • 然后如果首尾坐标相同,结束后就不用push进队列了

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>

using namespace std;

typedef pair<int,int> PII;

const int N = 200010;

int a[N];

struct cmp{
    bool operator()(PII x, PII y){
        if (x.second - x.first == y.second - y.first){
            return x.first > y.first;
        }
        else{
            return x.second - x.first < y.second - y.first;
        }
    }
};

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        priority_queue<PII,vector<PII>,cmp> q;
        int n;
        scanf("%d",&n);
        q.push({1,n});
        int cnt = 0;
        while(!q.empty()){
            cnt ++;
            PII t = q.top();
            q.pop();
            if (t.first == t.second){
                a[t.first] = cnt;
            }
            else if((t.second - t.first + 1) % 2 == 1){
                int x = (t.second + t.first) / 2;
                a[x] = cnt;
                if (x - 1 >= t.first) q.push({t.first,x - 1});
                if (x + 1 <= t.second)q.push({x + 1,t.second});
            }
            else{
                int x = (t.second + t.first - 1) / 2;
                a[x] = cnt;
                if (x - 1 >= t.first) q.push({t.first,x - 1});
                if (x + 1 <= t.second)q.push({x + 1,t.second});
            }
        }
        for (int i = 1; i <= n; i++){
            printf("%d ",a[i]);
        }
        puts("");
    }
    return 0;
}


E题解题思路:

  • dp,不过自己也不会写,因为想不太明白状态
  • 首先,如果没有0或者有1个0,那么肯定输出 0
  • 然后我们考虑别的情况,情况分为2种,第一种是当前位置是开头,第二种是该位置不是开头(那么i >= k)
  • 第一种是开头的话,那么好考虑,让前面的值都变成0,然后当前位如果是0就需要变成1,如果是1就不需要变
dp[i] = a[i - 1] + (b[i] == 0);
  • 第二种情况就是i >= k,那么不是第一个点,前面肯定还有一个点,所以我们是从dp[i - k]转移过来的,这其中要将他们之间的1变为0,a[i - 1] - a[i -k + 1],然后需要把当前位变为1(如果原本是0的话,那么就不用换)
dp[i] = min(dp[i],(b[i] == 0) + dp[i - k] + a[i - 1] - a[i - k]);
  • 最后我们遍历,如果当前的位置是最后一个位置,那么后面的都要变成0
for (int i = 1; i <= n; i++){
    res = min(res,dp[i] + a[n] - a[i]);
}
  • 最后这里比较坑的是不要memset(超时超死我了)

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

const int N = 1000010;

int a[N], b[N], dp[N]; 
char st[N];

int main(){
    int t;
    scanf("%d",&t);
    while(t--){

    	int n, k;
    	scanf("%d%d",&n,&k);
    	scanf("%s",st);

    	for (int i = 0; i <= n; i++) {
    		dp[i] = 0;
    		a[i] = 0;
    		b[i] = 0;
    	}  

    	for (int i = 0; i < n; i++){
    		if (st[i] == '0'){
    			a[i + 1] = 0;
    		}
    		else{
    			a[i + 1] = 1;
    		}
    		b[i + 1] = a[i + 1];
    		a[i + 1] += a[i];
    	}

    	if (a[n] == 0 || a[n] == 1){
    		puts("0");
    		continue;
    	}

    	int res = 1e9;


    	for (int i = 1; i <= n; i++){
    		dp[i] = a[i - 1] + (b[i] == 0);
    		if (i - k >= 0) dp[i] = min(dp[i],(b[i] == 0) + dp[i - k] + a[i - 1] - a[i - k]);
    	}
    	
    	for (int i = 1; i <= n; i++){
    		res = min(res,dp[i] + a[n] - a[i]);
    	}
    	printf("%d\n",res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值