Educational Codeforces Round 97 (Rated for Div. 2)

太菜了,卡在C上以至于没有看到D。


A. Marketing Scheme

题意:如果 x mod a ≥ ⌊a/2⌋,那么这是好的。问在x∈[l,r]时,能否找到一个a,使得对于区间内任意的x都是好的。

对于区间左端点,a能取得的最大的情况,就是a=2*l了,那么只要r小于2*l就成立。

B. Reverse Binary Strings

题意:翻转01串,使之变成01交题串

两种理解:

1、翻转一次不会造成内部的变化,只改变首尾两点。那么我们找多少个需要被交换的0或者1就行了。

比如(111)那么就有两个1需要被翻转走,需要被交换的01个数取大就好。

        int  cnt1 = 0, cnt2 = 0;
        for(int i=1;i<s.length();++i)(
        {
            if(s[i]==s[i-1])
            {
                if(s[i]=='1') cnt1++;
                else cnt2++;
            }
        }
        cout<<max(cnt2,cnt1)<<endl;

2、目标串只有101010以及010101的形式,所以,既然翻转一次内部方向改变,内部交替不变,那么我再换一次内部就好了。

比如11101000的目标是10101010,那么只有2和6号位不匹配,那么我翻转2-6,再翻转3-5就能达成目标。

对于目标是101010,但当中的四位0101都不匹配,那么我只用翻转一次就好。

这时候就会有一个问题?当前串01010不匹配呢,翻转也不行的啊?考虑到其他地方会多一个1不匹配,所以不计也没关系。

那么如果一定要多次操作才能还原后面的1呢?那这时候目标串是010101不是更好吗。

证明不是很严谨,当时是看到队友a了,急中生智,看了一下跟别人的解法都不一样。

可能是错的。

memset(vis,0,sizeof(vis));
        ans=anss=0;
        scanf("%d",&n);
        scanf("%s",a+1);
        //10101010
        f=1;
        for(int i=1;i<=n;++i){
            if(a[i]-'0'==f){
                
            }
            else{
                ans++;
                if(!vis[i])    vis[i]=1;  //需要被交换
                if(vis[i-1])    ans--;   //前面一位也被交换,当前位不计贡献
            }
            f^=1;
        }
        memset(vis,0,sizeof(vis));
        f=0;
        for(int i=1;i<=n;++i){
            if(a[i]-'0'==f){
                
            }
            else{
                anss++;
                if(!vis[i])    vis[i]=1;
                if(vis[i-1])    anss--;
            }
            f^=1;
        }
        ans=min(ans,anss);
        printf("%d
",ans);

C. Chef Monocarp

题意:一分钟只能出一个餐,一开始所有餐都在烤炉里,每次出餐贡献+|t[i]−T|(T为当前时间),问让总贡献最小的方法。
(注:出餐时间不一定连续。

很显然是一个dp题,但是我没想到方程怎么写。

dp[i][j]表示第i号餐在j时间出来的最优解。

先排个序,因为时间是线性增长的,t[i]线性增长更优。

代码来自一位dalao

int dp[270][270*2],a[270],t,n;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)    scanf("%d",&a[i]);
        memset(dp,0x7f,sizeof(dp));
        for(int i=0;i<=2*n;++i)    dp[0][i]=0;
        sort(a+1,a+1+n);
        for(int i=1;i<=n;++i){
            for(int j=i-1;j<=2*n;++j){
                for(int k=j+1;k<=2*n;++k){
                    dp[i][k]=min(dp[i][k],dp[i-1][j]+abs(a[i]-k));
                } 
            } 
        }
        int ans=inf;
        for(int i=n;i<=2*n;++i)    ans=min(ans,dp[n][i]);
        printf("%d
",ans);
    }
    return 0;
} 

D. Minimal Height Tree

题意:给定树的bfs序,对任意节点的子节点按升序排列,问最小高度是多少?

如果理解不了,我就来画个图吧。

两个都是1 4 5 2 3的遍历顺序,明显第一幅图更优。

先看一下代码

int t,a[200007],q[200007],p,n;    //q记录每层的个数
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        memset(q,0,sizeof(q));
        q[0]=1;p=1;
        for(int i=1;i<=n;++i)    scanf("%d",&a[i]);
        for(int i=2;i<=n;++i){
            if(a[i]>a[i-1]){    //升序就加入当前层
                q[p]++;
            }
            else if(q[p-1]>1){   //看一下上一层子结点个数大于1,挂到上层那个点
                q[p-1]--;
                q[p]++;
            }
            else if(q[p-1]==1){   //上层个数==1,说明,上层已经没有空间加了
                p++;
                q[p]++;
            }
        }
        printf("%d
",p);
    }
    return 0;
} 

还理解不了就对图跑一遍,记住节点的子结点都必须升序。

E. Make It Increasing

感谢该博主https://www.cnblogs.com/werner-yin/p/solution-CF-1437E.html的题解。

以下会对该博主的代码加上自己的注释和理解,便于观看。建议点开双开。

#include<cstdio>
#include<cmath> 
#include<iostream> 
#include<cstring> 
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<iomanip>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N= 1e6+7;
int a[N],b[N],lock[N],d[N],last=0,pos=0,n,k;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=k;++i){
        scanf("%d",&b[i]);
        lock[b[i]]=1;
    }
    sort(b+1,b+1+k);
    for(int i=1;i<k;++i)
        if(a[b[i]]-b[i]>a[b[i+1]]-b[i+1]){    //保证后一位锁定位之差大于前一位锁定位之差 
            printf("-1
");                    //因为严格上升,且上升的差值大于等于1 
            return 0;                        //比如三号位和六号位被锁,那么4 6的值是不合法的 
        }
    /*
        要求的是改几个位子使之成为严格上升序列。
        按一般的思路,找一下最长的严格上升子序列。
        原长度-找到的长度就行。
        但是这样有一个问题,我们不能保证找的子序列中间能改出来。
        比如样例一,我们能找到1 2 3 5.但是 2 3 之间的两个1 我们没有办法。
        所以我们先减去当前位的值。使之满足严格
        然后找最长不下降就行了。 
    
    */ 
    for(int i=1;i<=n;++i)    a[i]-=i;        //保证严格上升,且上升的差值大于等于1 
    for(int i=1;i<=n;++i){
        if(pos==0||a[i]>=d[pos]){
            d[++pos]=a[i];
            if(lock[i])    last=pos;        //如果新加入的点是锁定位,标记他 
        }
        else{
            int p= upper_bound(d+1,d+1+pos,a[i])-d;
            if(p<=last)    continue;    //如果找到替换位置在最后一个锁定位之前 
            d[p]=a[i];                //那么如果更换了,锁定就消失了。所以不能更换。 
            if(lock[i])    last=p,pos=p;
        }
    }
    printf("%d
",n-pos);
    return 0;
}
原文地址:https://www.cnblogs.com/PdrEam/p/13898961.html