scau 1138 代码等式 ( 并查集 )

5 篇文章 0 订阅
2 篇文章 0 订阅

 

1138 代码等式

 

时间限制:500MS  内存限制:65536K
提交次数:59 通过次数:21

题型: 编程题   语言: 无限制

 

Description

一个代码等式就是形如x1x2...xi=y1y2...yj,这里xi和yj是二进制的数字(0或1)或者是一个变量(如英语中的小写字母)。每一个变量都是一个有固定长度的二进制代码。例如:
a,b,c,d,e是变且它们的长度分别是4,2,4,4,2。考虑等式:1bad1=acbe,这个等式共有16组解。现要求任给一个等式,计算一共有多少组解。
(变量最多26个,长度和不超过10000)


 

输入格式

第一行数N为变量个数;
第二行N个数,为每个变量的位数
第三行为一个等式

 

输出格式

输出解的个数,无解输出0

 

 

 

 

 

输入样例

5
4 2 4 4 2
1bad1=acbe

 

 

 

 

 

输出样例

16

 

 

 

 

 

题意:

给你一个等式,等式两端是01串,也包含小写字母。
小写字母可能代表多个位。
求满足等式的解有多少个。




解题思路:
首先,每个字母代表多个位,要找到一种唯一而方便的映射,方便后续操作。下面是映射到一段连续的数字上:


用数组len存下每个字母的长度(len[0]表示‘a’,len[1]表示‘b’,以此类推)。
同时用数组begin记下每个字母的起始位置。(0,1的位置用来表示0和1。)显然字母‘a’的起始位置为2,即begin[0]=2;字母‘b’的起始位置begin[1]=begin[0]+len[0]。。。
即begin[i]=begin[i-1]+len[i-1](i>0)
这样就把字母映射到了连续的数字上。


然后把等式分成两边,按照上面的方法,拆除一位一位的,每位用一个数字来表示,如len[0]=4,len[1]=2,则a可用数字2,3,4,5表示,b可用数字6,7表示。此时对于等式“a=1b0”可以拆成2,3,4,5=1,6,7,0。

拆开后,首先判断两边的位数是否相等,不等的话等式肯定不成立。


接下来,两边在同一位置的数表示他们相等,可以放到同一个集合。这里可以采用并查集这种数据结构,详细的介绍请自行搜索。


在并查集中,两个元素在同一集合中,当且仅当他们有相同的根。在一开始初始化的时候,每个元素都是单独的一个集合,他们的根是本身。用数组Father表示就是Fathe[i]=i。
当发现i和j在同一位置时,即i和j相等,他们所在的集合也相等,可用f1、f2表示i、j的根,如果f1==f2说明他们已在同一集合中,不等的话执行Father[f1]=f2或Father[f2]=f1可将两个集合合并。
根据这个,相信你能想到,找出某个元素的根的做法。
当元素很多时,需要进行路径压缩,减少寻根的步骤,不过本题不需要,具体做法请自行思考。


最后,所有的数字都会被分成几个集合,集合内的数字都是相等。除了那两个包含0和1的集合外,其余的集合数为t,他们都有两种可能(0或1),则答案为2^t。


需要注意的是,必须先判断0和1是否在同一个集合中,在的话,等式肯定不成立。

 

另外,2^t用int会溢出而wa,可转成long long。

 

 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
const int N=10000+10;
char tmp[N];
int Left[N],Right[N],Father[N];

using namespace std;

int getfather(int v,int &step)
{
    step=1;
    while(Father[v]!=v)
    {v=Father[v];step++;}
    return v;
}

int conbine(int v,int w)
{
    int s1,s2,f1,f2;
    f1=getfather(v,s1);
    f2=getfather(w,s2);

    if(f1==f2)
    {return 0;}

    if(s1<=s2)
    {
        Father[f1]=f2;
    }
    else
    {
        Father[f2]=f1;
    }
    return 1;
}

const int M=3020;  //用3020是因为2^10000结果是3000多位
int res[M],t[M];

void add(int a[],int b[])
{
    int e;
    e=0;
    memset(t,0,sizeof(t));
    for(int i=0;i<M;i++)
    {
        t[i]=a[i]*2+e;
        if(t[i]>9)
        {
            t[i]-=10;
            e=1;
        }
        else
        e=0;
    }
    for(int i=0;i<M;i++)
    b[i]=t[i];
}

void _pow(int n)
{
    if(!n)
    {
        printf("0\n");
        return;
    }
    memset(res,0,sizeof(res));
    res[0]=1;
    for(int i=0;i<n;i++)
    {
        add(res,res);
    }
    int i;


    for(i=M-1;i>0;i--)
    if(res[i])
    break;
//    printf("%d\n",i);
    for(;i>=0;i--)
    printf("%d",res[i]);
    printf("\n");
}

int main()
{
    int n,total,len[30],begin[30];
    scanf("%d",&n);

    begin[0]=2;
    total=0;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&len[i]);
        total+=len[i];
        if(i)
        begin[i]=begin[i-1]+len[i-1];
    }
    for(int i=0;i<total+2;i++)
    Father[i]=i;

    scanf("%s",tmp);
    int part=strstr(tmp,"=")-tmp;

    int left_cnt=0;
    for(int i=0;i<part;i++)
    {
        if(tmp[i]=='0'||tmp[i]=='1')
        {
            Left[left_cnt]=tmp[i]-'0';
            left_cnt++;
            continue;
        }
        int t=tmp[i]-'a';
        for(int k=0;k<len[t];k++)
        {
            Left[left_cnt]=begin[t]+k;
            left_cnt++;
        }
    }

    int right_cnt=0;
    for(int i=part+1;tmp[i];i++)
    {
        if(tmp[i]=='0'||tmp[i]=='1')
        {
            Right[right_cnt]=tmp[i]-'0';
            right_cnt++;
            continue;
        }
        int t=tmp[i]-'a';
        for(int k=0;k<len[t];k++)
        {
            Right[right_cnt]=begin[t]+k;
            right_cnt++;
        }
    }
/*
    for(int i=0;i<left_cnt;i++)
    printf("%d ",Left[i]);
    printf("\n");
    for(int i=0;i<right_cnt;i++)
    printf("%d ",Right[i]);
    printf("\n");
*/
    if(left_cnt!=right_cnt)
    {
        printf("0\n");
        return 0;
    }

    for(int i=0;i<left_cnt;i++)
    {
       if(conbine(Left[i],Right[i]))
       total--;        //total表示不包含0和1的集合数,当两个集合合并时,显然总的集合数减一。
    }
    //printf("total:%d\n",total);


    if(!conbine(0,1))
    printf("0\n");
    else
    cout<<(long long )pow(2.0,total)<<endl;
   // _pow(total);
   //一开始用数组模拟,过了之换成long long发现也可以过。
}

 

 

 

 

 

============

 

以前写的、、

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值