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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM