NOIP2017赛前模拟 (2017.10.17)考试总结

 得分:100+0+0
 本次考试相较于昨天的三道题;
 T1 没有什么变化,稍加分析就可以看出是差分,但是根据这么久的做题,对于差分主要是有两点需要注意(树上差分单独):
1、差分的边界一定要注意,到底有没有0,最后是n+1还是n(今天一位因为没有从0开始差分,被wuvin卡成0分);
2、注意加的区间,是两边都闭还是左闭右开,一定要注意。
 T2 难度有所增加,这道题教会了我,考试的时候在无法保证自己的“正解”的正确性时,一定要分段打代码,今天考试时我发现挂个链表,每次把单调递减的序列减一减,试了几个数据都对了,以为自己很稳,就直接交了,结果炸成0分(蒟蒻。。),所以一定要分段打,保证拿分;
 T3 区间DP直接弃疗,区间DP状态的选择在简单题比较明显,至于真正的难度(我也没作过几道区间DP,所以无法评价);DP题主要就是方程的考虑要全面,可以手玩一些小数据,找找不同的情况。
 

T2
 题意:给你一个序列,每次将序列中连续严格下降的子序列减去,直至最后只剩单调递增的序列,输出这个序列。
 题解:可以定义三个队列q1,q2,q3,然后每次将要减去的数丢进q1,将暂时不丢的扔进q2,q3中临时存储丢掉的数用来更新答案(其实q3并没有用,可以在丢的时候就更新答案)

#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int T,n,a[N],l[N],r[N];
bool b[N];

inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

inline void solve(){
    n=Readint();
    for(int i=1;i<=n;++i){
        a[i]=Readint();
        l[i]=i-1;r[i]=i+1;
    }
    r[0]=1;
    a[n+1]=0x3fffffff;
    queue<int>q,q2,q3;
    for(int i=1;i<=n;++i){
        if((a[l[i]]>a[i]||a[i]>a[r[i]])){
            q.push(i);
        }
    }

    while(!q.empty()){
        while(!q.empty()){
            int u=q.front();
            q.pop();
            q3.push(u);
            if(a[l[u]]<=a[u]&&l[u]>=0)q2.push(l[u]);
            if(a[u]<=a[r[u]]&&r[u]<=n)q2.push(r[u]);
        }
        while(!q3.empty()){
            int u=q3.front();
            b[u]=1;
            q3.pop();
            l[r[u]]=l[u];
            r[l[u]]=r[u];
        }
        while(!q2.empty()){
            int u=q2.front();
            q2.pop();
            if(!b[u]&&(a[l[u]]>a[u]||a[u]>a[r[u]])){
                q.push(u);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        if(!b[i]){
            ++ans;
        }
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;++i){
        if(!b[i]){
            cout<<a[i]<<" ";
        }
    }
    cout<<endl;
}
inline void clear(){
    memset(b,0,sizeof(b));
    memset(a,0,sizeof(a));
    memset(r,0,sizeof(r));
    memset(l,0,sizeof(l));
}

int main(){
//  freopen("sort.in","r",stdin);

    T=Readint();
    while(T--){
        solve();
        clear();
    }
    return 0;
}

T3:
 题意:给你一个01串,当三个及以上的相同字符连在一起时就会抵消掉,保证一开始没有三个相同的字符连在一起,现在要求你插入一些0和1,将整个字符串抵消完,求最小代价。
 T<=10,n<=200;
 读完题发现这是一道区间DP题,我们可以定义 dp[ i ][ j ]表示将区间[i,j]抵消完的最小代价 ,我们把相邻的且相同的和在一起,用num[i]表示它的点权;转移有四种:

 3-num[i]          i==j
 dp[i][k] + dp[k+1][j]     i<=k < j
 dp[i+1][k-1]+dp[k+1][j]   color[i]==color[j]==color[k] & num[i]+num[j]<4 & num[k]==1
 
dp[l+1][j-1] +max(0,3-num[i]-num[j])

一个小样例:1100110010110100110011
答案为3不是2

#include<bits/stdc++.h>
using namespace std;
const int N = 205;
int T,len,cnt,ch[N],num[N],dp[N][N];
char s[N];


inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

inline void solve(){        
    memset(dp,0x3f,sizeof(dp));
    memset(num,0,sizeof(num));
    scanf("%s",s+1);
    len=strlen(s+1);cnt=0;
    for(int i=1;i<=len;i++){
        if(s[i]!=s[i-1]) ch[++cnt]=s[i],num[cnt]=1;
        else num[cnt]++; 
    }
    for(int i=cnt;i>=1;i--)
      for(int j=i;j<=cnt;j++){
            if(i==j){dp[i][j]=3-num[i];continue;}
            if(ch[i]==ch[j]){
              dp[i][j]=dp[i+1][j-1]+max(0,3-num[i]-num[j]);
              if(num[i]+num[j]<4)
                for(int k=i+2;k<j;k+=2)
                  if(num[k]==1)
                    dp[i][j]=min(dp[i][j],
                    dp[i+1][k-1]+dp[k+1][j-1]);
            }
            for(int k=i;k<j;k++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
      }
    cout<<dp[1][cnt]<<endl;
}

int main(){
//  freopen("beans.in","r",stdin);

    T=Readint();
    while(T--){

        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值