Codeforces #306 Div 2 简要题解

A. Two Substrings

题目链接

http://codeforces.com/contest/550/problem/A

题目大意

给你一个字符串,问这个字符串里是否同时包含两个子串’AB’和’BA’

思路

题目范围很小,因此我们可以直接先暴力求出每个’AB’和’BA’所在的位置,然后暴力看是否存在一对’AB’和’BA’互相没有覆盖即可。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110000

using namespace std;

typedef pair<int,int>pr;

char s[MAXN];
int totA=0,totB=0;
pr prA[MAXN],prB[MAXN];

int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    {
        if(s[i]=='A'&&s[i+1]=='B')
        {
            prA[++totA]=make_pair(i,i+1);
            continue;
        }
        if(s[i]=='B'&&s[i+1]=='A')
        {
            prB[++totB]=make_pair(i,i+1);
            continue;
        }
    }
    for(int i=1,p=1;i<=totA;i++)
    {
        for(;p<=totB;p++)
            if(prB[p].first!=prA[i].first&&prB[p].second!=prA[i].second&& prB[p].second!=prA[i].first&&prB[p].first!=prA[i].second)
            {
                printf("YES\n");
                return 0;
            }
    }
    for(int i=1,p=1;p<=totB;p++)
    {
        for(;i<=totA;i++)
            if(prB[p].first!=prA[i].first&&prB[p].second!=prA[i].second&& prB[p].second!=prA[i].first&&prB[p].first!=prA[i].second)
            {
                printf("YES\n");
                return 0;
            }
    }
    printf("NO\n");
    return 0;
}

B. Preparing Olympiad

题目链接

http://codeforces.com/contest/550/problem/B

题目大意

给你 n 个数C1...Cn,问这个集合有多少个子集,使得子集中的数字之和在范围 [l,r] 内,且最大值和最小值之差大于等于 x

思路

直接暴力枚举数字的子集即可

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110
#define INF 0x3f3f3f3f

using namespace std;

typedef pair<int,int>pr;
typedef long long int LL;

int diff[MAXN],n,L,R,mindiff;

int main()
{
    scanf("%d%d%d%d",&n,&L,&R,&mindiff);
    for(int i=0;i<n;i++) scanf("%d",&diff[i]);
    int ans=0;
    for(int S=1;S<(1<<n);S++)
    {
        int minDif=INF,maxDif=-INF,sum=0;
        for(int i=0;i<n;i++)
            if(S&(1<<i))
            {
                sum+=diff[i];
                minDif=min(minDif,diff[i]);
                maxDif=max(maxDif,diff[i]);
            }
        if(sum>=L&&sum<=R&&maxDif-minDif>=mindiff) ans++;
    }
    printf("%d\n",ans);
    return 0;
}

C. Divisibility by Eight

题目链接

http://codeforces.com/contest/550/problem/C

题目大意

给你一个长度不超过100位的十进制数,问是否可以从中删除一些数位,使得修改后的数字可以被8整除,并输出一组可行解。

思路

注意到长度大于等于四位的8的倍数的特征:末三位数构成的不含前导零的数字是八的倍数。

因此最终我们最多只需要从这个数字里提取3个数位来构成一个新的数字。

因此我们可以O(n)枚举从这个数字里选1个数位构成一个新的数字, O(n2) 枚举从这个数字里选2个数位构成一个新的数字, O(n3) 枚举从这个数字里选3个数位构成一个新的数字,找出一组合法解就输出答案。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110
#define INF 0x3f3f3f3f

using namespace std;

char s[MAXN];
int cnt[12],num[MAXN],len;
bool deled[MAXN];
int ans[110];
bool isBeishu[110000];

void solve1()
{
    int tot=0;
    for(int i=1;i<=len;i++)
        for(int j=i+1;j<=len;j++)
            for(int k=j+1;k<=len;k++)
            {
                if(!num[i]) continue;
                int nownum2=num[i]*100+num[j]*10+num[k];
                if(isBeishu[nownum2])
                {
                    printf("YES\n%d\n",nownum2);
                    return;
                }
            }
    for(int i=1;i<=len;i++)
        for(int j=i+1;j<=len;j++)
        {
            if(!num[i]) continue;
            int nownum2=num[i]*10+num[j];
            if(isBeishu[nownum2])
            {
                printf("YES\n%d\n",nownum2);
                return;
            }
        }
    for(int i=1;i<=len;i++)
        if(isBeishu[num[i]])
        {
            printf("YES\n%d\n",num[i]);
            return;
        }
    printf("NO\n");
}

void solve2()
{
    for(int S=1;S<(1<<len);S++) //!!!!!
    {
        int nownum=0;
        for(int i=1;i<=len;i++)
            if(S&(1<<(i-1)))
            {
                nownum*=10;
                nownum+=num[i];
            }
        if(isBeishu[nownum])
        {
            printf("YES\n%d\n",nownum);
            return;
        }
    }
    printf("NO\n");
}

int main()
{
    for(int i=0;i<=20000;i+=8) isBeishu[i]=true;
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1;i<=len;i++) num[i]=s[i]-'0',cnt[num[i]]++;
    /*if(cnt[0])
    {
        printf("YES\n0\n");
        return 0;
    }*/
    int rest=0;
    if(len>=4)
        solve1();
    else solve2();
    return 0;
}

D. Regular Bridge

题目链接

http://codeforces.com/contest/550/problem/D

题目大意

要求你构造一个简单无向图,使得图里最少包含一条桥边,且图里每个点度数均为 K

思路

存在多条桥边的情况下,我们只需要保留其中一条桥边,根据这条桥边把整个图划分成AB两个联通块,显然两个联通块是对称的,这样我们只需要构造其中一个联通块即可,在构造过程中,也可以把其他的桥边构造出来。

手玩一下可以发现K为奇数才有解。

考虑构造这个联通块,块中有一个点度数为 K1 ,其他点度数均为 K ,根据握手定理,这个联通块的边数为nK+K12(nK),度数为 K 的点的个数必须为偶数个,才合法。因此这个联通块点数最少为K+2

然后下面的问题就变为,给定了 K+2 个点的度数,构造一个图。这是非常经典的问题,在WC 2015上讲过。具体做法是,每次让剩余度数最大的点,按照剩余度数降序排序,连接其他点,直到这个点的剩余度数用完。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110
#define INF 0x3f3f3f3f

using namespace std;

int K;
vector<pair<int,int> >vec;
int degree[MAXN],rank[MAXN];

bool cmp(int a,int b)
{
    return degree[a]>degree[b];
}

int main()
{
    scanf("%d",&K);
    int T=K+2;
    if(!(K&1))
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    if(K==1)
    {
        printf("2 1\n1 2\n");
        return 0;
    }
    printf("%d %d\n",(K+2)<<1,(K+2)*K);
    for(int i=1;i<=T;i++) degree[i]=K;
    for(int i=1;i<=T;i++) rank[i]=i;
    degree[1]--;
    for(int i=1;i<=T;i++)
    {
        sort(rank+1,rank+T+1,cmp);
        for(int j=2;j<=degree[rank[1]]+1;j++)
        {
            vec.push_back(make_pair(rank[1],rank[j]));
            degree[rank[j]]--;
        }
        degree[rank[1]]=0;
    }
    printf("%d %d\n",1,T+1);
    for(int i=0;i<vec.size();i++)
    {
        printf("%d %d\n",vec[i].first,vec[i].second);
        printf("%d %d\n",vec[i].first+T,vec[i].second+T);
    }
    return 0;
}

E. Brackets in Implications

题目链接

http://codeforces.com/contest/550/problem/E

题目大意

定义一个 运算:

00=1

01=1

10=0

11=1

要在一个表达式里加上一些括号,使得表达式最后的计算结果变为0

思路

要想让表达式变为0,必须让表达式的后面是0,前面为1,因此我们可以这样构造

(a1(a2(a3...)))an

其中 an 必须为0,而且可以发现,如果 an 不为0,则绝对无解,我们无论怎样构造括号都无法找出一种解法,因为 an 、或者说是被 an 计算出来的值,永远都在一个表达式的右边而非左边,这样的话表达式的值肯定为1

然后我们要保证 (a1(a2(a3...))) 部分为1,要想让表达式为1,要么表达式左右两边都是0,要么表达式右边为1。从最里面的括号向外面计算,最里面的括号要么两个都是0,要么右边的 an1 是1。考虑一般的情况,即 an1=1 ,那么可以发现除了 an1,an 是有限制的情况外,其他项均无限制,合法的表达式的每一项应该是这个样子的:

xxxx...xxx10

而若最里面的括号两个都是0,则除了 an2,an1,an 是有限制的情况外,其他项均无限制,合法的表达式的每一项应该是这个样子的:
xxxx...xx000

而其他情况下,合法的表达式的每一项应该是这个样子的:
xxxx...xxx0111...100

排除掉上述三种合法情况后,剩下的非法情况就只有一种了:

1111...100

找出第一个非0的表达式项的位置,然后特判无解就好了

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 110000
#define INF 0x3f3f3f3f

using namespace std;

int n,val[MAXN];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    if(n==1)
    {
        if(val[1]==1) printf("NO\n");
        else printf("YES\n0\n");
        return 0;
    }
    if(val[n]==1)
    {
        printf("NO\n");
        return 0;
    }
    int p;
    for(p=1;p<=n-1;p++)
        if(val[p]!=1)
            break;
    if(p==n-1&&val[n-1]==0&&val[n]==0)
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    for(int i=1;i<=n-2;i++)
        printf("(%d->",val[i]);
    printf("%d",val[n-1]);
    for(int i=1;i<=n-2;i++)
        printf(")");
    printf("->0\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值