训练赛5(ICPC全国邀请赛—校内选拔赛 )

导语

运气好没有因罚时掉到第5,终于能出去打一次比赛

涉及的知识点

贪心、模拟、字符串、数学、DP、几何、思维、栈

链接:ICPC全国邀请赛—校内选拔赛

题目

A( CodeForces 1276A )

题目大意:给出一个字符串s,去掉最少的字符使得该串中不存在连续三个字符能构成"one"或"two",输出每个去掉字符的位置,多种答案输出一种

思路:本来想使用KMP,但是发现本题给的模式串并不是对称的,而且很小,所以应该可以直接暴力,统计s中有几个two和one即可,对于twone这个字符串单独讨论

代码

#include <bits/stdc++.h>

using namespace std;
int len,t;
char str[2121212];
deque<int>Q;
int main() {
    scanf("%d\n",&t);
    while(t--) {
        scanf("%s",str+1);
        len=strlen(str+1);
        for(int i=1; i<=len; i++) {
            string tmp;
            for(int j=0; j<3; j++)//获取连续三个字符构成的字符串
                tmp+=str[i+j];
            if(tmp=="two") {
                Q.push_back(i+1);//存下标,去掉中间字符就行了
            }
            if(tmp=="one") {
                if(!Q.empty()&&Q.back()==-1) {
                    Q.pop_back();
                    continue;
                }
                Q.push_back(i+1);
            }
            for(int j=3; j<5; j++)
                tmp+=str[i+j];
            if(tmp=="twone") {
                Q.pop_back();
                Q.push_back(i+2);
                Q.push_back(-1);//存一个标记,代表需要去掉重复值,因为后面必定会扫一个one
            }
        }
        getchar();
        printf("%d\n",Q.size());
        len=Q.size();
        for(int i=0; i<len; i++) {
            printf("%d%c",Q.front(),i==len-1?'\n':' ');
            Q.pop_front();
        }
        if(len==0)
            putchar('\n');
    }
    return 0;
}

B(计蒜客 A1616 )

题目大意:两队lol队员,每队5人,每队可禁用5个英雄,共100个,先给出一个5*100的01矩阵代表我方拥有人物情况,s[i][j]表示第i个人是否有第j个人物,1有0无,询问共有多少种对局情况(敌方假定有全部人物)

思路:对局情况由四个部分组成:我方选择、敌方选择、我方禁止、敌方禁止,那么总对局数就等于这四部分之积,后三部分的积是可以求出的: A 95 5 × C 90 5 × C 85 5 A_{95}^5×C_{90}^5×C_{85}^5 A955×C905×C855,分别对应敌方选择,我方禁止,敌方禁止,经过计算与取余可以得到值为531192758,之后采用动态规划的方法来计算选择方案数,可得状态转移方程。

1.s[i][j]为0,dp[i][j]=dp[i][j-1],第j个人物没有,直接选下一个
2.s[i][j]为1,dp[i][j]=dp[i][j-1]+dp[i-1][j-1],第j个人物有,变成可选可不选,累和方案

对我方5人需要求全排列,然后每个排列进行一次dp,因为每个排列的不同也算一种方案

最后累和可选方案,乘计算取余的值即可

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
char square[6][121];
ll dp[6][121],data[6],mod=1e9+7,num=531192758,ans;
int main() {
    while(~scanf("%s",square[1]+1)) {
        data[1]=1;
        for(int i=2; i<=5; i++) {
            scanf("%s",square[i]+1);
            data[i]=i;
        }
        do {
            memset(dp,0,sizeof(dp));
            for(int i=1; i<=100; i++) {
                dp[1][i]=dp[1][i-1];
                if(square[data[1]][i]=='1')
                    dp[1][i]++;
            }//必须初始化,否则后面的dp过程无法成立
            for(int i=2; i<=5; i++) {
                for(int j=1; j<=100; j++)
                    if(square[data[i]][j]=='0')//不可选
                        dp[i][j]=dp[i][j-1]%mod;
                    else//可选可不选
                        dp[i][j]=(dp[i][j-1]+dp[i-1][j-1])%mod;
            }
            ans=(ans+dp[5][100])%mod;
        } while(next_permutation(data+1,data+6));
        printf("%lld\n",ans*num%mod);
        ans=0;
    }
    return 0;
}

C(AtCoder agc031_a )

题目大意:给出一个长度为N的字符串,现给出本题中子串的定义:如果两个子串的各自的字符来源于原串的不同位置,则视为两字符串不同,并且两子串中字符的位置符合原串中的相对位置,求出没有重复字符的子串个数

思路:因为原串的顺序是固定的,所以可以认为取出来的字符串是自动排序的,即无需考虑排列问题,那么只需要考虑每个字符的组合问题了,记录每种字符出现的次数,每种字符选取一个位置即可,还要考虑不选的情况

代码

#include <bits/stdc++.h>
using namespace std;
long long N,num[27],ans=1,mod=1e9+7;
char ch;
int main() {
    scanf("%lld",&N);
    getchar();
    while((ch=getchar())!='\n')
        num[ch-'a']++;//记录数值
    for(int i=0; i<27; i++)
        ans=(ans*(num[i]+1))%mod;//排列组合
    printf("%lld",ans-1);
    return 0;
}

D(CodeForces 764B )

题目大意:给出n个立方体,每个立方体被赋值,现执行操作,在第i步时,翻转第i个到第n-i+1个盒子(包括边界),直到i ≤ \le n-i+1,现给出最终的翻转结果,求初始序列

思路:模拟过程即可,找规律可以发现每次只是换了边界值,注意数据规模

代码

#include <bits/stdc++.h>

using namespace std;
int a[212121],n;
int main() {
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=(n+1)/2; i+=2)
        swap(a[i],a[n-i+1]);
    for(int i=1; i<=n; i++)
        printf("%d%c",a[i],i==n?'\n':' ');
    return 0;
}

E(CodeForces 494A)

题目大意:给出一个由)、(、#三个字符组成的字符串,#可以变成至少一个),问该字符串能否括号匹配完,如果能输出每个#变成的)数量

思路:将字符串从后往前遍历,将碰到的第一个#用来满足之后)不足的情况,将其他#变为一个),最后判断是否匹配完

代码

#include <bits/stdc++.h>

using namespace std;
char str[121212];
int len,l,r,flag,ans;
vector<int>v;
int main() {
    scanf("%s",str+1);
    len=strlen(str+1);
    for(int i=len; i>=1; i--) {
        if(str[i]==')'||str[i]=='#') {
            if(l>0) {
                if(l>r&&flag==0) {
                    printf("-1");
                    return 0;
                } else {
                    if(l>r) {
                        ans+=l-r;
                        r=0;
                    } else
                        r-=l;
                    l=0;
                }
            }
            if(str[i]=='#')
                flag++;
            r++;
        } else
            l++;
    }
    if(l>0) {
        if(l>r&&flag==0) {
            printf("-1");
            return 0;
        } else {
            if(l>r) {
                ans+=l-r;
                r=0;
            } else
                r-=l;
            l=0;
        }
    }
    if(r==0)
        for(int i=1; i<=flag; i++)
            printf("%d\n",i==flag?1+ans:1);
    else
        printf("-1");
    return 0;
}

F(CodeForces 777A )

题目大意:三个杯子,一开始只有杯子里盖了一个球,现在知道第奇数次换0和1杯,偶数次换1和2杯,知道了最后求在哪,问一开始球在哪

思路:找规律,根据三种不同的情况判断即可

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
char data[3][6]={'0','1','2','2','1','0','1','0','0','1','2','2','2','2','1','0','0','1'};
int main() {
    ll n,t;
    scanf("%lld%lld",&n,&t);
    ll x=n%6;
    for(int i=0; i<=2; i++)
        if(data[i][x]==t+'0') {
            printf("%d",i);
            break;
        }
    return 0;
}

G( AtCoder agc031_b )

题目大意:给出N个数,可以进行0或多次操作,每次操作为将一段左右端点相等的区间全都变为左右相等的值,询问最后有多少种不同的序列

思路:设dp[i]表示当序列长度为i时,有多少种不同的序列,对于第i个位置,有两个选择,操作或不操作,操作,则获得上一个相等值位置的值,不操作,则获得相邻的前一个位置的值,如果上一个相等值位置正好相邻,直接赋值即可,具体看代码

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
ll N,val[212121],sum[212121],dp[212121]={1},mod=1e9+7;
//val记录值,sum记录当前值的上一个相等位置解的数量
//dp记录当序列长度为i时,有多少种不同的颜色方案
int main() {
    scanf("%lld",&N);
    for(int i=1; i<=N; i++)
        scanf("%lld",&val[i]);//扫入颜色
    for(int i=1; i<=N; i++) {
        if(val[i]==val[i-1]) {
            dp[i]=dp[i-1];//如果相邻相等,状态转移
            continue;
        }
        dp[i]=(dp[i-1]+sum[val[i]])%mod;
        //如果非相邻相等,记录方案数,该位置方案数有两个来源
        //选择变颜色,获得上一个相等位置的方案数,选择不变,获得前一个位置
        sum[val[i]]=dp[i];
    }
    printf("%lld",dp[N]);
    return 0;
}

H(CodeForces 749B )

题目大意:给出三个点坐标,求出能和这三个点组成平行四边形的第四个点有几个,并求出坐标

思路:平行四边形性质

代码

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int>pr;
pr data[3];
int main() {
    for(int i=0; i<3; i++)
        scanf("%d%d",&data[i].first,&data[i].second);
    printf("3\n");
    for(int i=0; i<3; i++)
        printf("%d %d\n",data[i].first+data[(i+1)%3].first-data[(i+2)%3].first,data[i].second+data[(i+1)%3].second-data[(i+2)%3].second);
    return 0;
}

I(计蒜客 A1428 )

题目大意:给出n个字符串,按输入顺序从每个字符串中选取一个后缀首尾拼接,求出一个字典序最小的字符串

思路:贪心,从最后一个字符串开始选取字典序最小的后缀,将后缀拼在前一个字符串上构成新串,以此类推

代码

#include <bits/stdc++.h>

using namespace std;
string str[212121];
int T,n,len[212121];
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=1; i<=n; i++) {
            cin >>str[i];//录入数据
            len[i]=str[i].size();//记录长度
        }
        for(int i=n; i>=1; i--) {
            int pos=1,length=str[i].length();//更新选取下标和当前构成的字符串的长度
            for(int j=1; j<=len[i]; j++)//随着j的增大,右边的比较字符串在移位,每次移位后都有自动填充
                if(str[i].substr(pos-1,length-pos+1)>str[i].substr(j-1,length-j+1))//找到相等长度下后缀最小下标
                    pos=j;
            str[i-1]+=str[i].substr(pos-1,length-pos+1);
        }
        cout <<str[0]<<endl;
        str[0]="";
    }
    return 0;
}

J(CodeForces 254B )

题目大意:输入n组数,每组数为截止月份+日期+这个任务需要的人数+需要的任务时间

思路:将天数转化为数字,并作为下标,记录每天的人数,获取最大需求量

代码

#include <bits/stdc++.h>

using namespace std;
int mon[14]= {0,31,28,31,30,31,30,31,31,30,31,30,31},day[1212],month[15],ans,n;
int main() {
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    scanf("%d",&n);
    for(int i=1; i<=12; i++)
        month[i]=month[i-1]+mon[i];//记录每个月末到起始的天数
    for(int i=1; i<=n; i++) {
        int m,d,p,t;
        scanf("%d%d%d%d",&m,&d,&p,&t);
        m--;//相对于起始的偏移量,如1月内就不能以31天来算,
        for(int j=month[m]+d+100-t; j<month[m]+d+100; j++) {
            day[j]+=p;//对于当前任务,从最开始到结束,中间的天数都要增加人数
            //以下标来代表天数
            ans=max(ans,day[j]);//获取最大需求量
        }
    }
    printf("%d",ans);
    return 0;
}

参考文献

  1. ACM-ICPC 2017 Asia Xi’an——LOL(全排列+dp)
  2. Colorful Subsequence
  3. B - Reversi
  4. CodeForces - 254B:Jury Size(思维、模拟)
  5. ACM-ICPC 2017 Asia Qingdao J. Suffix (二分 HASH LCP或者暴力)aa
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值