N - DAG优化SDUT

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

本题是使用DAG图进行代码的优化,这个题难就难在怎么让计算机实现这个DAG图,然后筛选无用的代码段做到化简的目的。针对例题进行理解,我会在注释中解释的尽量详细。

理解注释的前提是理解DAG图中结点的表示方法,结点的正下方以及最右方分别代表什么,以及如何根据DAG图重建代码。

#include<bits/stdc++.h>
using namespace std;
string s;
int flag[110]= {0};//flag数组全部初始化为0,flag数组在完成DAG图后,进行代码优化的时候才用到 
char ans[1100][1100];//建立一个二维数组,其中行代表第几个表达式,而每一行都有6列,用来存放具体的表达式(最后一列是'\0',表示结束) 
int num=0;//贯穿整个代码的全局变量
int n;
struct node {//创建一个结构体 
	int l=-1,r=-1;//l,r表示DAG图中的左,右子树 
	char id;//id则表示每一个结点的的值,可以是数字,或者运算符,或者变量名 
	vector<char>val;//Val表示代码基本块中等号左边的T的下标值 
} t[110];
//就比如说一个表达式A=B+C,在DAG图中,建立两个结点n_0,n_1,结点正下方分别存为B,C,之后再建立一个结点,n_2的正右方用来存放A。
//A的左子树指向B,即l=0(n_0);A的右子树指向C,即r=1(n_1)
//n_0的id值为B,n_1的id值为C,而n_2的id值为+
//n_0,n_1的val值为null,n_2的val值为A
//综上:l,r分别代表左右子树结点n的下标值,id表示每个n结点正下方对应的字符,Val则表示每个n结点正右方的字符。
//从此处跳转到 57行主函数进行阅读 
int use(int x,char ch) {//use函数相当于判断除了最底层结点之外,其他结点的正右方的Val值
//而t[i].id==ch只可以判断最底层结点的正下方的id值 
	int len=t[x].val.size();//len等于n结点正右方字符串的长度 
	for(int i=0; i<len; i++) {//如果正右方存在ch,返回1,否则返回0 
		if(t[x].val[i]==ch)return 1;
	}
	return 0;
} 
int add(char ch) {
	for(int i=num-1; i>=0; i--) {//num初始值为0 
		if(t[i].id==ch||use(i,ch))return i;//for循环的目的是用于判断在之前所构造的DAG图中是否出现了ch,出现过直接返回结点下标i
		//t[i].id==ch只用于判断DAG图中的最下层结点,更高层结点是否出现过需要调用use(i,ch)函数判断 
		//从此处跳转到19行use函数 
	}
	t[num].id=ch;//如果没出现过结点,则新建一个结点,此时的num是最新的值,不会出现重复赋值的情况(32行) 
	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) {//比如出现了A=B+C与D=B+C,此处判断出等号右边相等,则相等部分忽略 
			t[i].val.push_back(ch);//只保留等号左边不相等的部分,相当于在DAG图中A存在的结点的正右方加入一个字符D 
			return;//相当于A和D共享一个n结点 
		}
	}
	t[num].id=op;//如果没有重复出现,则新建n结点,结点正下方存入op值 
	t[num].l=l;//连接左子树 
	t[num].r=r;//连接右子树 
	t[num].val.push_back(ch);//将表达式所赋值的字符推入容器 
	num++;//num继续更新 
	return;//返回主函数阅读 
}
int dfs(int x) {//进行递归 
	if(t[x].l!=-1&&t[x].r!=-1) {//如果某个结点存在左右子树,flag[x]=1表示要用到这个表达式 
		flag[x]=1;
		dfs(t[x].l);//继续遍历,直到最底层的n结点 
		dfs(t[x].r);
	}
}
int main() {
	cin>>n;//输入n的值 
	for(int i=0; i<n; i++) {//n代表行数,从0到n-1行逐步遍历 
		cin>>s;//输入表达式 
		int l=add(s[2]);//每次输入表达式的2号下标的字符记为左子树的值,调用add函数,得到结点n的下标(开头结构体下方有解释) 
		int r=add(s[4]);//每次输入表达式的4号下标的字符记为右子树的值,调用add函数,得到结点n的下标(开头结构体下方有解释)  
		//从此处跳转到27行add函数进行阅读 
		add_op(s[3],s[0],l,r);//每次输入的表达式3号下标为运算符,0号下标为表达式所赋值到的字符
		//运算符,接受运算的字符,左子树的下标,右子树的下标传入add_op函数中进行判断 
		//从此处跳转到add_op函数进行阅读 
	}
	for(int i=0; i<num; i++) {//以上是DAG图的建立,建立好DAG图后开始根据DAG图写重建之后的代码 
		if(t[i].l!=-1&&t[i].r!=-1) {//如果某个结点存在左右子树 
			ans[i][0]=t[i].val[0];//以下是对代码重写,重建后的表达式从0-4进行数组的转存,5是结束符 
			ans[i][1]='=';
			ans[i][2]=t[t[i].l].val.size()>0?t[t[i].l].val[0]:t[t[i].l].id;
			ans[i][3]=t[i].id;
			ans[i][4]=t[t[i].r].val.size()>0?t[t[i].r].val[0]:t[t[i].r].id;
			ans[i][5]='\0';
		}
	}
	for(int i=num-1; i>=0; i--) {//因为要删除无用变量,只保留AB,所以分别写入AB的for循环判断 
		if(ans[i][0]=='A') {
			dfs(i);//跳转到dfs函数 
			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;
}

上面的代码好像A不了了,老师修改数据了,下面的代码可以A

AC代码:

#include <bits/stdc++.h>
using namespace std;
string s;
int flag[110];
char ans[110][110];
int n,num=0;
struct node
{
    int l=-1,r=-1;
    char id;
    vector<char>val;
}t[110];
int find(int x,char ch)
{
    for(char c:t[x].val)
        if(c==ch)return 1;
    return 0;
}
int add(char ch)
{
    for(int i=num-1;i>=0;i--)
        if(ch==t[i].id||find(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++;
}
void dfs(int x)
{
    if(t[x].l!=-1)
    {
        flag[x]=1;
        dfs(t[x].l);
        dfs(t[x].r);
    }
}
char exist(int x)
{
    char ans=0;
    for(char c:t[x].val)
        if(c=='A'||c=='B')ans=c;
    return ans!=0?ans:t[x].val[0];
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>s;
        int l=add(s[2]);
        int r=add(s[4]);
        add_op(s[3],s[0],l,r);
    }
    for(int i=0;i<num;i++)
    {
        if(t[i].l!=-1)
        {
            ans[i][0]=exist(i);
            ans[i][1]='=';
            ans[i][2]=t[t[i].l].val.size()>0?exist(t[i].l):t[t[i].l].id;
            ans[i][3]=t[i].id;
            ans[i][4]=t[t[i].r].val.size()>0?exist(t[i].r):t[t[i].r].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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值