• 2020第十一届蓝桥杯软件类省赛第二场C/C++ 大学 B 组 E: 七段码(DFS,二进制枚举+并查集判重)


    【问题描述】
    小蓝要用七段码数码管来表示一种特殊的文字。

    上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a, b, c, d, e, f, g。
    小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符 的表达时,要求所有发光的二极管是连成一片的。
    例如: b 发光,其他二极管不发光可以用来表达一种字符。
    例如: c 发光,其他二极管不发光可以用来表达一种字符。这种 方案与上 一行的方案可以用来表示不同的字符,尽管看上去比较相似。
    例如: a, b, c, d, e 发光, f, g 不发光可以用来表达一种字符。
    例如: b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光 的二极管没有连成一片。
    请问,小蓝可以用七段码数码管表达多少种不同的字符?

    答案:

    80

    1:

    暴力数

    dfs实现指数型枚举,然后自行根据是否连通一个一个数

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n;
    int vis[20];
    void dfs(int x)
    {
        if(x>n)
        {
            for(int i=1;i<=n;i++)
                if(vis[i])
                    cout<<i<<" ";
                    cout<<endl;
            return ;
        }
        vis[x]=1;
        dfs(x+1);
        vis[x]=0;
        dfs(x+1);
        
    }
    int main()
    {
        cin>>n;
    //    cout<<endl;
        dfs(1); 
        return 0;
    }

    2:

    如果上面那个写不好,可能会出现重复情况。下面这个是自行map去重代码:

    #include<iostream>
    #include<cstring>
    #include<map>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e2+10;
    int vis[11];
    int all=7;
    int cnt,sum=0;
    string s="0000000000";
    map<string,int>mp;
    int dfs(int x)
    {
        if(x==cnt+1)
        {    
            string s2;
            for(int i=1;i<=all;i++)
                s2+=s[i];
            mp[s2]++;
            if(mp[s2]==1)
            {sum++;
                for(int i=1;i<=all;i++)
                    if(s[i]!='0')
                     cout<<s[i];
                cout<<endl;
            }
            return 0;
        //    cout<<sum<<endl;
        } 
        for(int i=1;i<=all;i++)
        {
            if(s[i]=='0')
            {
                s[i]=i+'0';
                dfs(x+1);
                s[i]='0';
            }    
        }
    }
    int getlcm(int a,int b)
    {
        return a*b/__gcd(a,b);
    }
    int main()
    {    
        cnt=1;//cnt==1,2,3,4,5,6,7。自行更换值。 
        dfs(1);
    //    cout<<sum<<endl;
    }

    3:

    二进制枚举+并查集判联通

    1:首先说明:

    (1):1<<7的意思是,把1左移七位。换成二进制就是:10000000

    那么<(10000000)(二进制)的,就是0位~6位的所有情况。、

    (2):第78行:if( i&(1<<j)  )

    假如 i 的二进制和 1<<2的二进制分别为:

    1010100

    0000100

    如果if结果为真,说明i的第2位为1,即题目里的:发光

    所以可以根据这个if,来枚举所有 i 二进制中为1的位置

    2:并查集判联通。

    这个就没得说了,根据图,一个一个连上就行。

    每次判断vis[i]==1而且pr[i]==i的有几个,1个,就说明本次 i 的二进制只有一个连通块,符合要求,cnt++;

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e2+10;
    int pr[11],vis[11];
    void init(){memset(vis,0,sizeof(vis));
        for(int i=0;i<=8;i++)
            pr[i]=i;
    }
    int find(int x){
        if(x!=pr[x])return pr[x]=find(pr[x]);
        return x;
    }
    void add(int a,int b)
    {
        int fa=find(a),fb=find(b);
        if(fa!=fb)
            pr[fa]=fb;
            return ;
    }
    void check(int x)
    {
        vis[x]=1;
        if(x==0)
        {
            if(vis[1])add(0,1);
            if(vis[5])add(0,5);
            if(vis[6])add(0,6);
        }
        if(x==1)
        {
            if(vis[0])add(1,0);
            if(vis[6])add(1,6);
            if(vis[2])add(1,2);
        }
        if(x==2)
        {
            if(vis[1])add(2,1);
            if(vis[3])add(2,3);
            if(vis[6])add(2,6);
        }
        if(x==3)
        {
            if(vis[2])add(3,2);
            if(vis[4])add(3,4);        
        }
        if(x==4)
        {
            if(vis[5])add(4,5);
            if(vis[6])add(4,6);
            if(vis[3])add(4,3);
        }
        if(x==5)
        {
            if(vis[0])add(5,0);
            if(vis[4])add(5,4);
            if(vis[6])add(5,6);
        }
        if(x==6)
        {
            if(vis[1])add(6,1);
            if(vis[2])add(6,2);
            if(vis[4])add(6,4);
            if(vis[5])add(6,5);
        }
        return ;
    }
    int main()
    {
        int cnt=0;
        for(int i=1;i<(1<<7);i++)    //枚举所有排列 
        {
            init();//初始化 
            for(int j=0;j<=6;j++)
            {
                if(i&(1<<j))//1<<j的二进制表示,出现的第一个1与st中某个1同位置
                {
                    check(j);//加入连通 
                }
            }
            int ans=0;
            for(int j=0;j<=6;j++)
            {
                if(vis[j]&&pr[j]==j)
                    ans++;
            }
            if(ans==1)    cnt++;
        }
        cout<<cnt<<endl;
        return 0;
    }
  • 相关阅读:
    python爬虫统计上证指数周、月涨跌现象
    python每日一题:采用正则表达式,beautifulsoap,xpath爬取网站数据
    谈股市与月份的关系
    python之正则表达式
    python每日一题:使用代理服务器爬虫
    python之cookie使用
    python每日一题:爬虫入门之利用xpath查找网页元素节点
    python每日一题:制作网页,与女朋友的点点滴滴
    【Java基础】Java11 新特性
    【Java基础】Java10 新特性
  • 原文地址:https://www.cnblogs.com/liyexin/p/13893257.html
Copyright © 2020-2023  润新知