N - DAG优化

N - DAG优化
Description
大家都学过了代码优化,其中有一个DAG优化,这次我们就练习这个操作。

Input
输入第一行为一个整数n(n < 100),表示该组输入的表达式的个数

之后n行为表达式,每个变量为一个字母,表达式仅包括二元运算 + - * /

例如:A=B+C

Output
通过构造DAG图,进行代码优化,只需要保留AB,删除无用变量,删除变量时,尽量保留最早出现的变量。

PS:保证AB的值不同

Sample
Input
3
A=B+C
B=B+B
A=C+C
Output
B=B+B
A=C+C

ans[i][0],ans[i][2],ans[i][4]这三个位置都需要尽量出现A或B即使A或B不是最早出现的变量
要求符合这种条件的代码,可以试试以下样例

2
E=C+D
A=C+D
--------
A=C+D//正确输出 ,错误输出是什么都没有
3
E=C+D
A=C+D
B=A+C
-------------
A=C+D
B=A+C//正确输出
/*
E=C+D 错误输出
B=E+C
*/

AC的代码如下

#include <stdio.h>
#include <iostream>
#include <vector>
#include <memory.h>
using namespace std;

string s[110];
int n;
struct node//DAG图中的节点
{
    int l = -1;//左孩子
    int r = -1;//右孩子
    char id;
    vector<char>val;//节点值
} t[101];

int num = 0; //序号

int use(int x, char ch)//是否被使用过即在附加标记中出现
{
    int len = t[x].val.size();
    for(int i = 0; i< len; i++)
    {
        if(t[x].val[i] == ch)
            return 1;
    }
    return 0;
}

int add_node(char ch)//增加DAG图中的节点并返回序号
{
    for(int i = num - 1; i >= 0; i--)//倒着来,为了找在当前步骤前最后改动的
    {
        if(t[i].id == ch || use(i, ch))//曾经创建过或改动过就返回以前的序号
            return i;
    }
    t[num].id = ch;//创建新节点
    return num++;
}
void add_op(char op, char ch, int l, int r)//增加操作节点
{
    for(int i = num - 1; i >= 0; i--)
    {
        if(t[i].id == op && t[i].l == l &&  t[i].r == r)//当运算符和两个变量都相等时
        {
            t[i].val.push_back(ch);//不用创建新的操作,在相同的那个操作的附加标记处添加
            return ;
        }
    }
    t[num].id = op;
    t[num].l = l;
    t[num].r = r;
    t[num].val.push_back(ch);
    num++;
    return;
}
int flag[101];//标记当前序号下是否输出
char ans[1010][1010];//输出的表达式存在这里
void dfs(int x)
{
    if(t[x].l != -1 && t[x].r != -1)
    {
        flag[x] = 1;
        dfs(t[x].l);
        dfs(t[x].r);
    }
}
int main()
{
    cin>>n;
    memset(flag, 0, sizeof(flag));
    for(int i = 0; i < n; i++)
    {
        cin>>s[i];
        int l = add_node(s[i][2]);//记录左右儿子的序号
        int r = add_node(s[i][4]);
        add_op(s[i][3], s[i][0], l, r);//生成
    }
    /*找出内部节点即能生成表达式的节点
        内部节点是左右叶子为空
    */
    //ans[i][0],ans[i][2],ans[i][4]这三个位置都需要尽量出现A或B即使A或B不是最早出现的变量
    for(int i = 0; i < num; i++)
    {
        if(t[i].l!=-1&&t[i].r!=-1)
        {
            int f=0;
            for(int j=0;j<t[i].val.size();j++)
            {
                if(t[i].val[j]=='A') f=1;
                if(t[i].val[j]=='B') f=2;
            }
            if(f==1) ans[i][0]='A';
            else if(f==2) ans[i][0]='B';
            else ans[i][0]=t[i].val[0];

            ans[i][1]='=';

            int l=t[i].l;
            int r=t[i].r;
            /*左枝*/
            if(t[l].val.size()>0)
            {
                f=0;
                for(int j=0;j<t[l].val.size();j++)
                {
                    if(t[l].val[j]=='A') f=1;
                    if(t[l].val[j]=='B') f=2;
                }
                if(f==1) ans[i][2]='A';
                else if(f==2) ans[i][2]='B';
                else ans[i][2]=t[l].val[0];
            }
            else ans[i][2]=t[l].id;
            /*右枝*/
            if(t[r].val.size()>0)
            {
                f=0;
                for(int j=0;j<t[r].val.size();j++)
                {
                    if(t[r].val[j]=='A') f=1;
                    if(t[r].val[j]=='B') f=2;
                }
                if(f==1) ans[i][4]='A';
                else if(f==2) ans[i][4]='B';
                else ans[i][4]=t[r].val[0];
            }
            else ans[i][4]=t[r].id;

            ans[i][3]=t[i].id;
            ans[i][5]='\0';

        }
    }

    /*由题意只需保留AB,就只将表达式左边有A或B的式子输出
       以下就是在寻找式子左端为A,B的表达式,并将表达式中用到的两个因子输出
       如果两个因子是经过其他表达式产生的
    */
    for(int i = num - 1; i >= 0; i--)
    {
        if(ans[i][0]  == 'A')
        {
            dfs(i);
            break;
        }
    }
    for(int i = num - 1; i >= 0; i--)
    {
        if(ans[i][0]  == 'B')
        {
            dfs(i);
            break;
        }
    }
    for(int i = 0; i < num; i++)
    {
        if(flag[i])
            printf("%s\n", ans[i]);
    }
    return 0;
}
//用flag[i]标记的方法,而不是直接输出是因为:顺序和避免重复输出


简化版:(提取了个函数)

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
struct node
{
    int l=-1,r=-1;
    char id;
    vector<char> val;
}p[1001];
int num;
bool use(int t,char ch)
{
    for(int i=0;i<p[t].val.size();i++)
    {
        if(p[t].val[i]==ch) return true;
    }
    return false;
}
int add_node(char ch)
{
    for(int i=0;i<num;i++)
    {
        if(p[i].id==ch||use(i,ch))
        {
            p[i].val.push_back(ch);
            return i;
        }
    }
    p[num].id=ch;
    return num++;
}
void add_op(char op,int l,int r,char ch)
{
    for(int i=0;i<num;i++)
    {
        if(p[i].id==op&&p[i].l==l&&p[i].r==r)
        {
            p[i].val.push_back(ch);return ;
        }
    }
    p[num].id=op;
    p[num].l=l;p[num].r=r;
    p[num++].val.push_back(ch);
}
int flag[1001];
void dfs(int t)
{
    if(p[t].l!=-1||p[t].r!=-1)
    {
        flag[t]=1;
        dfs(p[t].l);
        dfs(p[t].r);
    }
}
char fun(node t)
{
    for(int i=0;i<t.val.size();i++)
    {
        if(t.val[i]=='A') return 'A';
        if(t.val[i]=='B') return 'B';
    }
    return t.val[0];
}
int main()
{
    int n;
    cin>>n;char s[101][101];
    for(int i=0;i<n;i++)
    {
        cin>>s[i];
        int l=add_node(s[i][2]);
        int r=add_node(s[i][4]);
        add_op(s[i][3],l,r,s[i][0]);
    }
    char ans[101][101];
    for(int i=0;i<num;i++)
    {
        if(p[i].l!=-1&&p[i].r!=-1)
        {
            ans[i][0]=fun(p[i]);
            ans[i][1]='=';
            int l=p[i].l;
            int r=p[i].r;

            ans[i][2]=p[l].val.size()>0?fun(p[l]):p[l].id;
            ans[i][4]=p[r].val.size()>0?fun(p[r]):p[r].id;
            ans[i][3]=p[i].id;
            ans[i][5]='\0';

        }
    }
    for(int i=num-1;i>=0;i--)
    {
        if(ans[i][0]=='A')
        {
            dfs(i);break;
        }
    }
    for(int i=num-1;i>=0;i--)
    {
        if(ans[i][0]=='B')
        {
            dfs(i);break;
        }
    }
    for(int i=0;i<num;i++)
    {
        if(flag[i])
            cout<<ans[i]<<endl;
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值