算法竞赛入门经典(第2版)-刘汝佳-第六章解题源码(C++语言)(部分)

来源:互联网 发布:单片机isp程序下载接口 编辑:程序博客网 时间:2024/06/06 00:23

例题6-1(TLE/WA)

本题出现TLE的情况,因为在本题,我使用了太多的STL。在结果方面估计也会有些问题,因为对于lock/unlock的机制理解不清楚,现提出自己的错误代码。

关于双端队列的用法可以参看点击打开链接

#include<iostream>#include<sstream>#include<string>#include<cstring>#include<queue>#include<deque>#include<map>using namespace std;int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int m;cin>>m;while(m--){queue<string> prog[15];queue<int> lq;deque<int> rq;map<string,int> ot;//存储每个语句的执行时间 int var[26];//存储变量memset(var,0,sizeof(var));int ap,qu,lockflag=0;//存储程序数量和一次执行程序的时间。 cin>>ap>>ot["="]>>ot["print"]>>ot["lock"]>>ot["unlock"]>>ot["end"]>>qu;for(int i=1;i<=ap;i++){string s;while(getline(cin,s)&&s!="end"){if(!s.empty()&&s!="\n"){prog[i].push(s);}}prog[i].push("end");rq.push_back(i);}while(!rq.empty()){int rqf,time=qu,lqin=0;rqf=rq.front();rq.pop_front();string stmp,rtmp[3],statement;stmp=prog[rqf].front();stringstream ss(stmp);ss>>rtmp[0]>>rtmp[1]>>rtmp[2];if(rtmp[1]=="="){statement="=";}else{if(rtmp[0]=="print")statement="print";if(rtmp[0]=="lock")statement="lock";if(rtmp[0]=="unlock")statement="unlock";if(rtmp[0]=="end")statement="end";}int endflag=0;while(time>=ot[statement]&&!prog[rqf].empty()){if(statement=="="){stringstream ss(rtmp[2]);//字符串转int int itmp;//ss>>itmp;var[rtmp[0][0]-'a']=itmp;time-=ot["="];prog[rqf].pop();}if(statement=="print"){cout<<rqf<<": "<<var[rtmp[1][0]-'a']<<endl; time-=ot["print"];prog[rqf].pop();}if(statement=="lock"){time-=ot["lock"];if(lockflag==0){lockflag=1;prog[rqf].pop();}else{lq.push(rqf);lqin=1;break;} }if(statement=="unlock"){time-=ot["unlock"];prog[rqf].pop();lockflag=0;if(!lq.empty()){rq.push_front(lq.front());lq.pop();}}if(rtmp[0]=="end"){time-=ot["end"];prog[rqf].pop();endflag=1;break;}stmp=prog[rqf].front();rtmp[1].clear();stringstream ss(stmp);ss>>rtmp[0]>>rtmp[1]>>rtmp[2];if(rtmp[1]=="="){statement="=";}else{if(rtmp[0]=="print")statement="print";if(rtmp[0]=="lock")statement="lock";if(rtmp[0]=="unlock")statement="unlock";if(rtmp[0]=="end")statement="end";}}if(!endflag&&!lqin){rq.push_back(rqf);}}cout<<endl;}   return 0; } 

例题6-2

本题不多说,书中的分析和解法简洁明了。

#include<iostream>#include<stack>using namespace std;const int maxn=1000;int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int len;while(cin>>len&&len){int da[maxn];for(int i=0;i<len;i++){da[i]=i+1;}int fir;while(cin>>fir&&fir){int dc[maxn];stack<int> st;dc[0]=fir;for(int i=1;i<len;i++){cin>>dc[i];}int a=0,c=0,flag=1;while(c<len){if(da[a]==dc[c]){a++;c++;}else if(!st.empty()&&st.top()==dc[c]){c++;st.pop();}else if(a<len){st.push(da[a]);a++;}else{flag=0;break;}}if(flag)cout<<"Yes"<<endl;elsecout<<"No"<<endl;}cout<<endl;}return 0;} 

例题6-3

本题我采用map存储各个字母矩阵对应的行和列,而本题使用结构体来存行和列,用数字索引代替字母,具有一定的技巧性,而在中间过程的计算中,也直接不存中间过程的名字,只存储计算中间过程中的行和列。而我在中间过程中,将中间过程存储在map中。

#include<iostream>#include<stack>#include<map>#include<string>#include<sstream>using namespace std;int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);typedef pair<int,int> Pair;map<string,Pair> ma;int m;cin>>m;while(m--){string c;Pair rc;cin>>c>>rc.first>>rc.second;ma[c]=rc;}string s;getline(cin,s);//cout<<s<<endl;while(getline(cin,s)){//cout<<s<<endl;int ans=0,flag=1;stack<string> sc;for(int i=0;i<s.length();i++){if(s[i]==')'){string a,b;a=sc.top();sc.pop();b=sc.top();sc.pop();if(ma[a].first==ma[b].second){ans+=ma[a].first*ma[a].second*ma[b].first;Pair ab;ab.first = ma[b].first;ab.second = ma[a].second;string sab= a+b;ma[sab]=ab;sc.push(sab);}else{cout<<"error"<<endl;flag=0;break;}}else if(s[i]!='('){stringstream ss;ss<<s[i];string s1;ss>>s1;//cout<<s1;sc.push(s1);}}if(flag)cout<<ans<<endl;}return 0;} 

例题6-4

本题目的写法非常经典,技巧很多,建议逐步运行实现。实现链表的方式,和使用指针实现的不一样,但是效率更高。实际上,思想就是在插入某个元素的时候,要保证上一个元素指向自己,而自己要指向下一个元素(可能为0)。实际上,实现了在上一个元素和光标之间的插入操作。

#include<cstdio>#include<cstring>const int maxn=100000+5;int begin,last,pre,next[maxn];char s[maxn];int main(){freopen("datain.txt","r",stdin);while(scanf("%s",s+1)==1){int n=strlen(s+1);last=pre=0;next[0]=0;for(int i=1;i<=n;i++){if(s[i]=='['){pre=0;}else if(s[i]==']'){pre=last;}else{next[i]=next[pre];//cur存储的上一个元素的位置。 next[pre]=i;//确定上一个元素的下一个元素的位置。 if(pre==last) last=i;pre=i;}}for(int i=next[0];i!=0;i=next[i]){if(s[i]!=']'&&s[i]!='[')printf("%c",s[i]);}printf("\n");}return 0;} 

例题6-5(TLE/uDebug测试全通过)

本题我采用了双端链表,写的方式更像我们所学的数据结构那样,但是遭遇TLE,所有uDebug测试数据都通过。现在就不在纠结这道题了。如果要避免TLE,就要在源头上用最简单的数据结构存储,使用更加低级的操作。比如不要使用结构体,输入输出尽量使用C语言操作。我的写法感觉在算法上,已经不能够再精简了。

#include<iostream>using namespace std;const int maxn=100000+5;struct Boxs{long long  left;long long  right;};int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);Boxs boxs[maxn]; long long m,n,rnd=1;while(cin>>n>>m){long long  sum=0; //初始化boxsboxs[0].left=0;boxs[0].right=1;boxs[n+1].left=n;boxs[n+1].right=0;for(long long i=1;i<=n;i++){boxs[i].left=i-1;boxs[i].right=i+1;} while(m--){int t;cin>>t;if(t==1){long long x,y;cin>>x>>y;if(boxs[x].right!=y){boxs[boxs[x].left].right=boxs[x].right;boxs[boxs[x].right].left=boxs[x].left;boxs[boxs[y].left].right=x;boxs[x].left=boxs[y].left;boxs[x].right=y;boxs[y].left=x;}}else if(t==2){long long x,y;cin>>x>>y;if(boxs[y].right!=x){boxs[boxs[x].left].right=boxs[x].right;boxs[boxs[x].right].left=boxs[x].left;boxs[boxs[y].right].left=x;boxs[x].left=y;boxs[x].right=boxs[y].right;boxs[y].right=x;}}else if(t==3){long long x,y;cin>>x>>y;if(boxs[x].right!=y&&boxs[x].left!=y){long long tmp;boxs[boxs[x].right].left=y;boxs[boxs[x].left].right=y;boxs[boxs[y].right].left=x;boxs[boxs[y].left].right=x;tmp=boxs[x].right;boxs[x].right=boxs[y].right;boxs[y].right=tmp;tmp=boxs[x].left;boxs[x].left=boxs[y].left;boxs[y].left=tmp;}else{if(boxs[x].right==y){boxs[boxs[x].left].right=y;boxs[boxs[y].right].left=x;boxs[y].left=boxs[x].left;boxs[x].right=boxs[y].right;boxs[y].right=x;boxs[x].left=y;}else{boxs[boxs[x].right].left=y;boxs[boxs[y].left].right=x;boxs[x].left=boxs[y].left;boxs[y].right=boxs[x].right;boxs[x].right=y;boxs[y].left=x;}}}else if (t==4) {long long tmp;for(long long i=1;i<=n;i++){tmp=boxs[i].left;boxs[i].left=boxs[i].right;boxs[i].right=tmp;}boxs[boxs[0].right].right=n+1;boxs[boxs[n+1].left].left=0;tmp=boxs[0].right;boxs[0].right=boxs[n+1].left; boxs[n+1].left=tmp; }}int j=1;for(long long i=boxs[0].right;i!=0;i=boxs[i].right){if(j%2==1&&boxs[i].right!=0)sum+=i;j++;}cout<<"Case "<<rnd++<<": "<<sum<<endl;}return 0;} 

例题6-6

本题目见书中分析,书中给出两段代码,第一段TLE,第二段AC,但是第一段非常容易理解,第一段可以理解为穷举。

第一段:TLE

#include<cstdio>#include<cstring>const int maxn= 20 ;int s[1<<maxn];//<<位运算,表示1*(2^maxn); int main(){int m;scanf("%d",&m);while(m--){int D,I;while(scanf("%d%d",&D,&I)==2){memset(s,0,sizeof(s));int k,n=(1<<D)-1;//n为最大节点编号. for(int i=0;i<I;i++){k=1;while(true){s[k]= !s[k];k=s[k]?2*k:2*k+1;//k是变化的,本来k为真,为右边。//但是因为提前改变状态,所以k为假在右边。if(k>n) break; //循环结束条件。  }}printf("%d\n",k/2);}}return 0;} 

第二段:AC

#include<cstdio>#include<cstring>int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);long int D,I;int m;scanf("%d",&m);while(m--){while(scanf("%ld%ld",&D,&I)==2){long int k=1;for(int i=0;i<D-1;i++){if(I%2){k=k*2;I=(I+1)/2;}else{k=k*2+1;I/=2;}    } printf("%d\n",k);}}return 0;} 

例题6-7

本题就是典型的如果构建一个树,构建后进行宽度遍历(层次遍历)。主要注意对输入的处理方式,利用队列来进行树的宽度遍历。本题使用指针相对而言,比较好理解。
#include<cstdio>#include<cstring>#include<queue>#include<vector>using namespace std;const int maxn=1000;struct Node{bool have_value;int v;Node *left,*right;Node():have_value(false),left(NULL),right(NULL){}};bool failed;Node* root;char s[maxn]; //树节点的数据结构 //增加节点 Node* newnode(){return new Node();}//构建树 void addnode(int v,char* s){int n = strlen(s);Node* u = root; for(int i=0;i<n;i++){if(s[i]=='L'){if(u->left==NULL) u->left = newnode();u=u->left;}else if(s[i]=='R'){if(u->right==NULL) u->right = newnode();u=u->right;}}if(u->have_value) failed=true;u->v=v;u->have_value=true;}//输入处理 bool read_input(){failed=false;root=newnode();for(;;){if(scanf("%s",s)!=1) return false;if(!strcmp(s,"()")) break;int v;sscanf(&s[1],"%d",&v);//字符输入可以类比stringstream addnode(v,strchr(s,',')+1);}return true;}//使用队列辅助实现层次遍历(宽度有限遍历)bool bfs(vector<int> &ans){queue<Node*> q;ans.clear();q.push(root);while(!q.empty()){Node* u=q.front();q.pop();if(!u->have_value) return false;ans.push_back(u->v);if(u->left!=NULL) q.push(u->left);if(u->right!=NULL)q.push(u->right);}return true;} //释放空间 void remove_tree(Node* u){if(u==NULL) return;remove_tree(u->left);remove_tree(u->right);delete u;}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);while(read_input()){vector<int> ans;if(failed){printf("not complete");}else if(bfs(ans)){for(vector<int>::iterator it= ans.begin();it!=ans.end();it++){if(it==ans.end()-1){printf("%d",*it);}else{printf("%d ",*it);}}}else{printf("not complete");}printf("\n");remove_tree(root);}return 0;} 

本书还介绍了其他几种,构建一个树的方法,但是我个人还是认为第一种方法更加简单和易于理解。

现展示如下:

//使用数组+下标的方式实现二叉树。const int root=1;void newtree(){ left[root]=right[root]=0;have_value[root]=false;cnt=root; } int newnode(){int u=++cnt;left[u]=right[u]=0;have_value[u]=false;return u;}//使用结构体+指针Node* newnode(){Node * u = &node[cnt++];//将node[cnt++]的地址给u,意思是将u装入到node中,//此时并不是直接随机给u一个地址,new Node();node数据的定义肯定是 Node node[maxn]。 u->left=u->right=NULL;u->have_value=false;return u; } //内存池维护一个空闲列表  queue<Node*> freenodes;Node node[maxn];void init(){for(int i=0;i<maxn;i++){freenodes.push(&node[i]);}} Node* newnode(){Node *u = freenodes.front();freenodes.pop();u->left=u->right=NULL;u->have_value=false;return u;}void deletenode(Node* u){freenodes.push(u);}//实际上,如果没有特殊要求,使用new动态申请空间,使用delete释放空间,就如同最开始代码中生成和删除节点的方法。 

例题6-8

本题先通过后序遍历和中序遍历构建二叉树,在通过深度优先遍历,从根到叶子搜索每一条路径,然后选取最小权的路径。数据结构采用的为两个数字lch和rch来存储根的左右子树,构造树和深度优先遍历都采用了递归的手段。下面的代码为书中代码,我补加了注释,便于大家理解。

#include<iostream>#include<string>#include<sstream>#include<algorithm>using namespace std;const int maxv=10000+10;int in_order[maxv],post_order[maxv],lch[maxv],rch[maxv];int n;//读入数据函数 bool read_list(int *a){string line;if(!getline(cin,line)) return false;stringstream ss(line);n=0;int x;while(ss>>x){a[n++]=x;}return n>0;}//递归构建构建树 int build(int L1,int R1,int L2,int R2){//L1为中序遍历的开头,R1为中序遍历的结尾//L2为后序遍历的开头,R2为后序遍历的结尾if(L1>R1) return 0;//判断结束条件。 int root = post_order[R2];//循环寻找根位置 //根的位置在后序遍历的最后一个。 int p = L1;while(in_order[p]!=root) p++;int cnt = p-L1;//从中序遍历中获得左子树的节点数。 lch[root] = build(L1,p-1,L2,L2+cnt-1); //lch[root]存储的为以root为根的左子树的根 rch[root] = build(p+1,R1,L2+cnt,R2-1);//rch[root]存储的为以root为根的右子树的根 return root;} int best,best_sum;//深度优先遍历。 void dfs(int u,int sum){sum+=u;if(!lch[u]&&!rch[u])//如果以u为根的左子树和右子树都为空,就是枚举出所有的解来比较。 {if(sum<best_sum||(sum==best_sum&&u<best))//如果以u为根的左子树和右子树不为空。 寻找最小的sum {best=u;best_sum=sum;}}if(lch[u]) dfs(lch[u],sum);if(rch[u]) dfs(rch[u],sum);}//n为节点个数 int main(){freopen("datain.txt","r",stdin);while(read_list(in_order)){read_list(post_order);build(0,n-1,0,n-1);best_sum = 1000000000;dfs(post_order[n-1],0);//深度优先遍历,给出根的位置。 cout<<best<<endl;}return 0;} 

例题6-9

一开始对题目理解的不够完全,输出"YES"的条件为各个子天平都要平衡,而天平的权值为子天平权值的和。肯定是要采用递归的模式来进行。我的代码如下,比较好理解。但书中的代码更加精髓,我对书中代码进行了注释,便于理解学习。

我的代码:

#include<iostream>using namespace std;int sumwl=0,sumwr=0,flag=1;void readleft(){int wl,dl,wr,dr;cin>>wl>>dl>>wr>>dr;sumwl=sumwl+wl+wr;if(wl!=0&&wr!=0) {if(wl*dl!=wr*dr)flag=0;return;}if(wl==0){readleft();}if(wr==0){ readleft();}}void readright(){int wl,dl,wr,dr;cin>>wl>>dl>>wr>>dr;sumwr=sumwr+wl+wr;if(wl!=0&&wr!=0){if(wl*dl!=wr*dr)flag=0;return;} if(wl==0){readright();}if(wr==0){ readright();}}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int m;cin>>m;while(m--){int wl,dl,wr,dr;int suml,sumr;cin>>wl>>dl>>wr>>dr;if(wl==0){readleft();}else{sumwl=wl;}if(wr==0){readright();}else{sumwr=wr;}if(sumwl*dl==sumwr*dr&&flag){cout<<"YES"<<endl;}else {cout<<"NO"<<endl;}if(m!=0){cout<<endl;}sumwl=sumwr=0;flag=1;}return 0;} 

书中代码分析:

首先,使用递归必须明确递归结束的条件,显然题目中结束的条件为W1不为0,则结束左子树的递归,当W2不为0,则结束右子树的递归。而我们感兴趣的是子树是不是平衡的和子树的权值总和,这样才能计算自己是否平衡,所以,书中代码使用b1和b2来确定左右子树是否平衡,使用W1*D1==W2*D2来确定自己是否平衡,而权值则通过引用进行传递W为权值和,W1为左子树的权值,W2为右子树的权值。

#include<iostream>using namespace std;bool solve(int &W){int W1,D1,W2,D2;bool b1=true,b2=true;cin>>W1>>D1>>W2>>D2;if(!W1) b1=solve(W1);if(!W2) b2=solve(W2);W=W1+W2;return b1&&b2&&(W1*D1==W2*D2);}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int T,W;cin>>T;while(T--){if(solve(W)) cout<<"YES\n";else cout<<"NO\n";if(T) cout<<"\n";}return 0;}
运用递归要明确两个要点,第一个是结束的条件,第二个是需要递归确定的值,比如本题中的权重,如果需要知道总的权值,那么就需要知道左右子树权值之和。那么可以递归下去,先求得最底层的权值,再返回向上,进行计算。

例题6-10(uDebug测试数据通过,UVa RE)

本题我的思路是先将这个二叉树看成完全二叉树。这样我们只要知道层数,那么我们就可以计算出根的位置,在根的左边就根的位置-1,根的右边就根的位置+1。然后输出就可以得到答案。

#include<iostream>#include<cstring>using namespace std;const int maxn=1000000;int sumres[maxn];int sumn=1,cnt=1; int lf[maxn],rt[maxn],value[maxn];void build(int root,int n){int tmpn=n,leftvalue,rightvalue;cin>>leftvalue;if(leftvalue!=-1){tmpn++;int left = ++cnt;value[left]=leftvalue;lf[root]=left;if(tmpn>sumn)sumn=tmpn;build(left,tmpn);}tmpn=n;cin>>rightvalue;if(rightvalue!=-1){tmpn++;int right = ++cnt;value[right]=rightvalue;rt[root]=right;if(tmpn>sumn)sumn=tmpn;build(right,tmpn);}}void calres(int root,int code){sumres[code]+=value[root];if(lf[root]!=-1){calres(lf[root],code-1);}if(rt[root]!=-1){calres(rt[root],code+1);}}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int root,rootvalue,n=1,rnd=1;cin>>rootvalue;while(rootvalue!=-1){memset(lf,-1,sizeof(lf));memset(rt,-1,sizeof(rt));memset(sumres,0,sizeof(sumres));memset(value,0,sizeof(value));cnt=1;root=cnt;value[root]=rootvalue;sumn=1;build(root,n);int len;if(sumn>2){len=(1<<(sumn-1))+(1<<(sumn-2));calres(root,(len/2));}else if(sumn==2){calres(root,2);}elsesumres[1]=rootvalue;cout<<"Case "<<rnd++<<":"<<endl;int flag=0;for(int i=1;i<=len;i++){if(sumres[i]!=0){if(!flag){cout<<sumres[i];flag=1;}elsecout<<" "<<sumres[i];}}cout<<endl<<endl;cin>>rootvalue;}return 0;} 

例题6-11

本题一开始真不知道怎么做,所以借鉴了书中代码。我给书中的代码写了少许注释,以便于理解。

#include<cstdio>#include<cstring>const int len=32;const int maxn=1024+10;char s[maxn];int buf[len][len],cnt;//本题目两张图在进行输入时候,将结果输入到buf中。//将图片视为32*32的一个图 ,并使用r和c对每个方框进行编号后//放入buf中。 //w表示现在递归中的ch表示的格子个数。 void draw(const char*s,int &p,int r,int c,int w){char ch = s[p++];if(ch=='p'){draw(s,p,r,c+w/2,w/2);//1draw(s,p,r,c,w/2);//2draw(s,p,r+w/2,c,w/2);//3draw(s,p,r+w/2,c+w/2,w/2);//4}else if(ch=='f'){for(int i=r;i<r+w;i++)for(int j=c;j<c+w;j++)if(buf[i][j]==0){buf[i][j]=1;cnt++;}}}int main(){int T;scanf("%d",&T);while(T--){memset(buf,0,sizeof(buf));cnt=0;for(int i=0;i<2;i++){scanf("%s",s);int p=0;draw(s,p,0,0,len);}printf("There are %d black pixels.\n",cnt);}return 0;}

例题6-12

本题目主要是对八连块的理解,八连块和连通的数量没关系,只是表示可以上下左右斜上斜下等8个方向进行移动。通过dfs进行遍历,具体代码注释已经写好,可以参考学习。

#include<cstdio>#include<cstring>const int maxn=100+5;char pic[maxn][maxn];int m,n,idx[maxn][maxn];void dfs(int r,int c,int id){if(r<0||r>=m||c<0||c>=n) return;//行和列越界判断 if(idx[r][c]>0||pic[r][c]!='@') return;// idx[r][c]>0 表示已经被遍历过。 // 节约时间,快速返回。 idx[r][c]=id;for(int dr=-1;dr<=1;dr++)//dc和dr表示可以朝上下左右移动 for(int dc=-1;dc<=1;dc++)if(dr!=0||dc!=0) dfs(r+dr,c+dc,id);//dr和dc不能同时为0,如果同时为0,则原地不动。 } int main(){while(scanf("%d%d",&m,&n)==2&&m&&n)//接收行和列 {for(int i=0;i<m;i++) scanf("%s",pic[i]);//一行一行的接收,并存在一个字符串数组中。//实际上是将输入数据存入pic数组中。 memset(idx,0,sizeof(idx));int cnt=0;for(int i=0;i<m;i++)for(int j=0;j<n;j++)if(idx[i][j]==0&&pic[i][j]=='@') dfs(i,j,++cnt);//每个@进行一次深度优先遍历。idx[i][j]表示还没有被遍历。//意味着现阶段还没有加入其他八连块。 printf("%d\n",cnt);}return 0;}

例题6-13

本题步骤:

1. 先将输入的十六进制转化为二进制图像(先转化为十进制),其中0表示白色,1表示黑色。

2. 因为每个字符图像是一个四连块,将图像进行dfs,并将各个字符图像并从1开始编号。

3. 此时,图中的0,有些为空洞的0,有些为非空洞的0,判断每一个0的是否为空洞0,判断的方法为从元素为0的位置向上、向下、向左、向右搜索第一个大于0的元素,如果上下左右都相等且大于0,那么这个0元素就为空洞元素,否则为非空洞元素(判断是否在字符图像中),对从非空洞元素位置开始dfs,并标记为-1.这样就找到这张图中所有的空洞元素和非空洞元素。空洞元素标记为0.非空洞元素标记为-1.

4. 将空洞元素进行dfs,找到每个空洞元素属于的字符图像,实际上为dfs的边界元素(边界元素就为开始标记的字符图像的编号)。

5. 这样就可以通过每个字符图像的空洞数量来进行输出。

6. 代码有具体注释,可供参考。

#include<iostream>#include<cctype>#include<cstring>#include<algorithm>using namespace std;const int maxn=10000;int pic[maxn][maxn],idx[maxn][maxn];int r,c,bt;void dfs(int ir,int ic,int id){if(ir<0||ir>=r||ic<0||ic>=c) return;if(idx[ir][ic]>0||pic[ir][ic]!=1) return;idx[ir][ic]=id;dfs(ir+1,ic,id);dfs(ir-1,ic,id);dfs(ir,ic+1,id);dfs(ir,ic-1,id);}void dfsz(int ir,int ic,int id){if(ir<0||ir>=r||ic<0||ic>=c) return;if(idx[ir][ic]==-1||pic[ir][ic]!=0) return;idx[ir][ic]=id;dfsz(ir+1,ic,id);dfsz(ir-1,ic,id);dfsz(ir,ic+1,id);dfsz(ir,ic-1,id);}void dfsv1(int ir,int ic,int id){if(ir<0||ir>=r||ic<0||ic>=c) return;if(idx[ir][ic]==id) return;if(idx[ir][ic]>0){bt=idx[ir][ic];return;}idx[ir][ic]=id;dfsv1(ir+1,ic,id);dfsv1(ir-1,ic,id);dfsv1(ir,ic+1,id);dfsv1(ir,ic-1,id);}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);char input[205][55];int ans[100],rnd=1;char code[]="WAKJSD";while(cin>>r>>c&&r!=0&&c!=0){  memset(idx,0,sizeof(idx));memset(pic,0,sizeof(pic)); memset(ans,0,sizeof(ans));  int m=0;for(int i=0;i<r;i++) {cin>>input[i];int tmp,bi[4];m=0;for(int j=0;j<c;j++){memset(bi,0,sizeof(bi));//十六进制转化成十进制。if(isdigit(input[i][j])){tmp=input[i][j]-'0';}else{tmp=input[i][j]-'a'+10;}    //十进制转化成二进制。for(int n=3;n>=0;n--){bi[n]=tmp%2;tmp=tmp/2;if(tmp==0) break;}for(int n=0;n<4;n++){pic[i][m++]=bi[n];}}}c = m;//重新生成图片后的列数     int cnt=0;//字符的个数 //通过dfs将所有字符进行编号 for(int i=0;i<r;i++){for(int j=0;j<c;j++){if(pic[i][j]==1&&idx[i][j]==0){dfs(i,j,++cnt);}}}//将pic中非空洞的0标注为-1,保证数空洞的时候不会数错。//搜索每一个为非空洞的0,并进行dfs进行-1.//判断每一个0点 for(int i=0;i<r;i++){for(int j=0;j<c;j++){if(idx[i][j]==0){int up=-1,down=-1,left=-1,right=-1;//向上搜索for(int m=i;m>=0;m--){if(idx[m][j]>0){up=idx[m][j];break;}}//向下搜索for(int m=i;m<r;m++){if(idx[m][j]>0){down=idx[m][j];break;}}//向左搜索for(int m=j;m>=0;m--){if(idx[i][m]>0){left=idx[i][m];break;}}//向右搜索for(int m=j;m<c;m++){if(idx[i][m]>0){right=idx[i][m];break;}}//如果上下左右都相等的话,为空点。//否则不为空洞点。 if(!(left==right&&left==up&&up==down&&left!=-1)){dfsz(i,j,-1);}  }}}//for(int i=0;i<r;i++)//{//for(int j=0;j<c;j++)//{//cout<<idx[i][j]<<" ";//}//cout<<endl;//}////数空洞的数量int hcnt=-1;for(int i=0;i<r;i++){for(int j=0;j<c;j++){if(idx[i][j]==0){bt=0;dfsv1(i,j,--hcnt);ans[bt]++;}}}char res[100];for(int i=1;i<=cnt;i++){res[i]=code[ans[i]];}sort(res+1,res+cnt+1);cout<<"Case "<<rnd++<<": ";for(int i=1;i<=cnt;i++){cout<<res[i];} cout<<endl; }return 0;}

例题6-14

本题目将书中的代码补全,并进行了注释。书中进行判断是否前进的inside()函数,我不知道其中的含义,也没有写这个函数,UVa还是通过了。这里其实要注意的就是两个点1是存从初始状态到某个点的最短路长度,2是存BFS中的父节点。实际上,在进行BFS采用队列进行辅助。

#include<iostream>#include<string>#include<cstring>#include<queue>#include<vector>using namespace std;const int maxn=100;const char* dirs="NESW";const char* turns="FLR";int d[maxn][maxn][4];int has_edge[maxn][maxn][4][3];int r0,c0,r1,c1,r2,c2,dir; struct Node{int r,c,dir;Node(int r1=0,int c1=0,int dir1=0){r=r1;c=c1;dir=dir1;}}; Node p[maxn][maxn][4];int dir_id(char c){return strchr(dirs,c)-dirs;//查找字符串dirs中首次出现字符c的位置}int turn_id(char c){return strchr(turns,c)-turns;}//根据当前状态和转弯方式计算出来的后续状态。 const int dr[]={-1,0,1,0};const int dc[]={0,1,0,-1};//dc实际上表示为列上面的走向。dr为行上面的走向 Node walk(const Node& u ,int turn){int dir=u.dir;if(turn==1) dir=(dir+3)%4;//左转 if(turn==2) dir=(dir+1)%4;//右转 return Node(u.r+dr[dir],u.c+dc[dir],dir);}//从后到前的输出 void printf_ans(Node u){vector<Node> nodes;for(;;){nodes.push_back(u);if(d[u.r][u.c][u.dir]==0) {break;}u = p[u.r][u.c][u.dir];}//寻找父节点 nodes.push_back(Node(r0,c0,dir));int cnt = 10;for(int i=nodes.size()-1;i>=0;i--){if(cnt%10==0) printf(" ");printf(" (%d,%d)",nodes[i].r,nodes[i].c);if(++cnt%10==0)printf("\n");}if(nodes.size()%10!=0) printf("\n");}//has_edge是一个四维数组 void solve(){queue<Node> q;memset(d,-1,sizeof(d));Node u(r1,c1,dir);//起点 d[u.r][u.c][u.dir]=0;//初始状态到(r,c,dir)的最短路径 q.push(u);//然后将u压入栈内 while(!q.empty())//层次遍历是否结束 {Node u = q.front();q.pop();if(u.r==r2 && u.c==c2)//表示如果u已经到达终点 {printf_ans(u);//输出 return;}for(int i=0;i<3;i++)//3个方向 {Node v = walk(u,i);if(has_edge[u.r][u.c][u.dir][i]&&d[v.r][v.c][v.dir]<0)//has_edge表示这种前进方式是否能够进入。d[v.r][v.c][v.dir]表示还没有到达这个点.{d[v.r][v.c][v.dir]=d[u.r][u.c][u.dir]+1;p[v.r][v.c][v.dir]=u;q.push(v);} }}printf("  No Solution Possible\n");}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);string name;char cdir;while(cin>>name&&name!="END"){cout<<name<<endl;memset(has_edge,0,sizeof(has_edge));cin>>r0>>c0>>cdir>>r2>>c2;dir=dir_id(cdir);r1=r0+dr[dir];c1=c0+dc[dir];int tmpr,tmpc;while(cin>>tmpr&&tmpr){cin>>tmpc;char tmp;int tmpdt1i,tmpdt2i;while(cin>>tmp&&tmp!='*'){if(tmp=='N'||tmp=='E'||tmp=='W'||tmp=='S'){tmpdt1i=dir_id(tmp);}if (tmp=='L'||tmp=='R'||tmp=='F'){tmpdt2i=turn_id(tmp);has_edge[tmpr][tmpc][tmpdt1i][tmpdt2i]=1;}}}solve();}return 0;} 

例题6-15

本题使用DFS进行拓扑排序,是一个经典的写法。其思想就是,在一个无环有向图中,先找到拓扑排序中的最后一个点,最后一个点满足的条件就是它的执行不会影响到其他任务。也就是说别人只会影响它,它不会影响别人。迭代进行就可以求得拓扑排序。代码中有注释,但是如果算法还是理解不了,可以百度DFS+拓扑排序。很多网页提供更加详尽的解释。

#include<cstdio>#include<cstring>const int maxn=100+5;//c数组,0表示从未访问过,1表示已经访问并递归过它的子孙。//-1表示正在访问。int c[maxn]; int topo[maxn],t,n;int G[maxn][maxn]; bool dfs(int u){c[u]=-1;for(int v=1;v<=n;v++){if(G[u][v])//采用邻接矩阵存储图,表示u有指向v的边,表示u完成后v才能完成。 {if(c[v]<0) return false;//判断是否有环的存在 else if (!c[v]&&!dfs(v)) return false;//如果v已经被访问过,则退出。//如果没有访问,就进行深度遍历,进行访问。 }}c[u]=1;topo[--t]=u;return true;}bool toposort(){t=n;memset(c,0,sizeof(c));for(int u=1;u<=n;u++){if(!c[u])//没有进行访问的才进行访问 {if(!dfs(u)) return false;}}return true;}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int m;while(scanf("%d%d",&n,&m)&&n){memset(G,0,sizeof(G));while(m--){int i,j;scanf("%d%d",&i,&j);G[i][j]=1;}toposort();for(int i=0;i<n;i++){printf("%d",topo[i]);if(i<n-1)printf(" ");}printf("\n");}return 0;}

例题6-16

通过存在欧拉回路的条件进行判断,再使用dfs来判断是否连通。

#include<iostream>#include<string>#include<cstring>using namespace std;const int maxn=30;int G[maxn][maxn],visit[maxn],od[maxn],id[maxn];void dfs(int s){visit[s]=-1;for (int i=0;i<maxn;i++){if(G[s][i]==1&&!visit[i]){dfs(i);}}}int check(){int flag=1;for(int i=0;i<maxn;i++){if(visit[i]==0){flag=0;return flag;}}return flag;}int eulerroot(){int start=-1,end=-1,first;for(int i=0;i<maxn;i++){int tmp;if(od[i]!=0){first=i;}tmp=od[i]-id[i];if(tmp==1){if(start==-1)start=i;elsereturn -1;}else if(tmp==-1){if(end==-1)end=i;elsereturn -1;}else if(tmp!=0){return -1;}}if(start==-1){return first;}return start;}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int m;cin>>m;while (m--){memset(G,0,sizeof(G));memset(od,0,sizeof(od));memset(id,0,sizeof(id));memset(visit,-1,sizeof(visit));int n,start;cin>>n;while(n--){string tmp;cin>>tmp;int r,c;r=tmp[0]-'a';c=tmp[tmp.length()-1]-'a';G[r][c]=1;visit[r]=0;visit[c]=0;od[r]++;id[c]++;}start=eulerroot();if(start==-1)cout<<"The door cannot be opened."<<endl;else{dfs(start);int flag=check();if(flag){cout<<"Ordering is possible."<<endl;}elsecout<<"The door cannot be opened."<<endl;}}return 0;} 

例题6-17

本题目的难点还是在对输入数据的处理上,难点并不在数据结构,书中的代码也易于理解。

#include<cstdio>#include<cctype>#include<cstring>const int maxn =200+10;int n;char buf[maxn][maxn];void dfs(int r,int c){printf("%c(",buf[r][c]);if(r+1<n&&buf[r+1][c]=='|')//n为buf的总行数,这句话表示有子树 {int i=c;while(i-1>=0&&buf[r+2][i-1]=='-') i--;//找到---的左边界 while(buf[r+2][i]=='-'&&buf[r+3][i]!='\0'){if(!isspace(buf[r+3][i])) dfs(r+3,i);i++;}}printf(")");} void solve(){n=0;for(;;){fgets(buf[n],maxn,stdin);if(buf[n][0]=='#') break; else n++;}printf("(");if(n){for(int i=0;i<strlen(buf[0]);i++)if(buf[0][i]!=' '){dfs(0,i);break;}}printf(")\n");}int main(){//freopen("datain.txt","r",stdin);int T;fgets(buf[0],maxn,stdin);sscanf(buf[0],"%d",&T);while(T--) solve();return 0;}

例题6-20

本题书中数相当重要使用了两次BFS,具体详细的分析和解释可以参考。

点击打开链接

写的很详细。我将他的代码贴在下面。另外本题采用BFS采用邻接表的形式,数据结构中可以使用邻接表和邻接矩阵存图,具体将会在第六章的总结中呈现。

#include<cstdio>#include<vector>#include<queue>#include<cstring> using namespace std; //min()函数 #define max 100000#define inf 0x7ffffffftypedef struct ver{    int num, color;//边的另一端的节点编号 和 颜色     ver(int n,int c):num(n),color(c){}}Ver;int n,m,a,b,c;int d[max],res[max];//d记录每个点到终点的最短距离 res记录最短路的颜色bool vis[max],inqueue[max];//vis每个节点是否被访问过 inqueue标记节点是否加入了队列,防止重复加入 vector<Ver> edge[max];//邻接表记录图 void bfs(int start,int end){    memset(inqueue,0,n);    memset(vis,0,n);    int u,v,c;    queue<int> q;    q.push(start);    if(start==0){//用于正向bfs         memset(res,0,sizeof(int)*n);        while(!q.empty()){            u=q.front();q.pop();vis[u]=1;            if(u==n-1)return;            int minc=inf,len=edge[u].size();            for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v])minc=min(edge[u][i].color,minc);//获取所有路径中最小的颜色            for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v] && edge[u][i].color==minc && !inqueue[v])q.push(v),inqueue[v]=1; //若有多组颜色相同,且未入队,则将其入队             int index=d[0]-d[u];//获得当前步数对应的下标            if(res[index]==0)res[index]=minc;            else res[index]=min(res[index],minc);//获取最小颜色         }    }//用于反向bfs 构建层次图,找最短路     else while(!q.empty()){        u=q.front();q.pop();vis[u]=1;        for(int i=0,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){             d[v]=d[u]+1; //一定是头一次入队,这通过inqueue保证             if(v==0)return; //找到起点,退出             q.push(v);//如果不是起点,就把这个点入队             inqueue[v]=1;//入队标记         }    }}int main(){    while(scanf("%d%d",&n,&m)==2){        for(int i=0;i<n;i++)edge[i].clear();        memset(d,-1,sizeof(int)*n);d[n-1]=0;//注意初始化的细节        while(m--){            scanf("%d%d%d",&a,&b,&c);            if(a!=b){ //排除自环                edge[a-1].push_back(ver(b-1,c));                edge[b-1].push_back(ver(a-1,c));            }        }        bfs(n-1,0);//反向bfs        bfs(0,n-1);//正向bfs         printf("%d\n%d",d[0],res[0]);        for(int i=1;i<d[0];i++)printf(" %d",res[i]);        printf("\n");    }}

例题6-1

本题就是一个栈的使用,一定要注意输入的格式问题。

#include<iostream>#include<stack>#include<string>using namespace std;int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int n;cin>>n;getchar();while(n--){stack<char> sc;while(sc.size()) sc.pop();string s;getline(cin,s);bool flag = true;for(int i=0;i<s.length();i++){if(s[i]=='('||s[i]=='['){sc.push(s[i]);}   else if (!sc.empty()&&((s[i]==']'&&sc.top()=='[')||(s[i]==')'&&sc.top()=='(')))   {   sc.pop();   }else{flag=false;break;}     }if(sc.empty()&&flag)cout<<"Yes"<<endl;elsecout<<"No"<<endl;}return 0;} 

习题6-2

本题第一眼看是树,实际上和树关系不大,根据题意,我们可以知道在叶子节点分别可以编号成十进制的0-2^n,n为树深度,那么再根据输入的编码和树中给出的顺序进行一一对应,这样就输入的编码转化为10进制,直接就能得出答案。

#include<iostream>#include<string>#include<cstring>using namespace std;const int maxn=1000;int main(){int n,rnd=1;//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);while(cin>>n&&n){cout<<"S-Tree #"<<rnd++<<":"<<endl;int res[maxn],tim[n+1],decode[maxn];memset(res,-1,sizeof(res));memset(decode,-1,sizeof(decode));for(int i=1;i<=n;i++){string s;cin>>s;tim[s[1]-'0']=i;}for(int i=0;i< 1<<n;i++){char tmp;cin>>tmp;res[i]=tmp-'0';}getchar();//接收回车 int m;cin>>m;while(m--){for(int i=1;i<=n;i++){char tmp;cin>>tmp;decode[tim[i]]=tmp-'0';}int sum=0;for(int i=1;i<=n;i++){sum+=decode[i]*(1<<(n-i));;}cout<<res[sum];}cout<<endl<<endl;}return 0;} 

习题6-3

可以参照例题6-10进行,对于树而言,三种遍历方式非常重要,我也将会在第六章进行总结。

#include<iostream>#include<string>#include<cstring>using namespace std;const int maxn=1000;int lch[maxn];int rch[maxn];string preorder,inorder;int build (int L1,int R1,int L2,int R2)//L1R1为中序遍历,L2R2为先序遍 {if(L1>R1) return -1;int root = preorder[L2]-'A';int p=L1;while(inorder[p]-'A'!= root) p++;int cnt=p-L1;//计算左子树的数量 lch[root]=build(L1,p-1,L2+1,L2+cnt-1);rch[root]=build(p+1,R1,L2+cnt+1,R2);return root;}void postorder(int root){if(lch[root]!=-1) postorder(lch[root]);if(rch[root]!=-1) postorder(rch[root]);char tmp=root+'A';cout<<tmp;}int main(){ //freopen("datain.txt","r",stdin); string s; while(getline(cin,s)){memset(lch,-1,sizeof(lch));memset(rch,-1,sizeof(rch));int index=s.find(" ",0);preorder=s.substr(0,index);inorder=s.substr(index+1,s.length()-index-1);int len1=preorder.length();int len2=inorder.length();int root=build(0,len2-1,0,len1-1);postorder(root);cout<<endl; preorder.clear(); inorder.clear();}return 0;} 

习题6-4

本题目使用一次BFS就行。难度不大。为了使用队列方便,除了使用(x,y)坐标表示一个格子外,也将棋盘的左下角开始从左到右,从下到上由0-63进行编号。

#include<iostream>#include<string>#include<cstring>#include<queue>using namespace std;int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);string s;while(getline(cin,s)){int board[9][9],vis[9][9];queue<int> q;int x = s[0]-'a'+1;int y = s[1]-'0';memset(board,-1,sizeof(board));memset(vis,-1,sizeof(vis));board[x][y]=0;vis[x][y]=0;int index= 8*(y-1)+x-1;q.push(index);while(!q.empty()){index= q.front();y = index/8+1;x = index%8+1;q.pop();if(x+2<=8&&y+1<=8&&vis[x+2][y+1]==-1){board[x+2][y+1]=board[x][y]+1;vis[x+2][y+1]=0;index=8*(y)+x+1;q.push(index);}if(x+1<=8&&y+2<=8&&vis[x+1][y+2]==-1){board[x+1][y+2]=board[x][y]+1;vis[x+1][y+2]=0;index= 8*(y+1)+x;q.push(index);}if(x+2<=8&&y-1>=1&&vis[x+2][y-1]==-1){board[x+2][y-1]=board[x][y]+1;vis[x+2][y-1]=0;index= 8*(y-2)+x+1;q.push(index);}if(x+1<=8&&y-2>=1&&vis[x+1][y-2]==-1){board[x+1][y-2]=board[x][y]+1;vis[x+1][y-2]=0;index= 8*(y-3)+x;q.push(index);}if(x-2>=1&&y-1>=1&&vis[x-2][y-1]==-1){board[x-2][y-1]=board[x][y]+1;vis[x-2][y-1]=0;index= 8*(y-2)+x-3;q.push(index);}if(x-2>=1&&y+1<=8&&vis[x-2][y+1]==-1){board[x-2][y+1]=board[x][y]+1;vis[x-2][y+1]=0;index= 8*(y)+x-3;q.push(index);}if(x-1>=1&&y-2>=1&&vis[x-1][y-2]==-1){board[x-1][y-2]=board[x][y]+1;vis[x-1][y-2]=0;index= 8*(y-3)+x-2;q.push(index);}if(x-1>=1&&y+2<=8&&vis[x-1][y+2]==-1){board[x-1][y+2]=board[x][y]+1;vis[x-1][y+2]=0;index= 8*(y+1)+x-2;q.push(index);}} x = s[3]-'a'+1; y = s[4]-'0'; cout<<"To get from "<<s[0]<<s[1]<<" to "<< s[3]<<s[4]<<" takes "<<board[x][y]<<" knight moves."<<endl;} return 0;}

习题6-5

本题目采用BFS,但是不仅要考虑到达每个点的最短距离,还要考虑到到达这个点的时候,剩余的可以连续穿越障碍的步数。这样到达可能出现到达某点的时候,离起点的距离不同,剩余可以连续跨越障碍的不通,但是根据BFS的运行规则,第一个到达终点的肯定就是最短路径的。程序如下,注意队列的清空。

#include<iostream>#include<queue>#include<cstring>using namespace std;const int maxn=100;struct Node{int x,y,d,k;//k存储还能前进几步Node(){};Node(int x1,int y1,int d1,int k1):x(x1),y(y1),d(d1),k(k1){};};int row,col,maxk,board[maxn][maxn],vis[maxn][maxn][maxn];int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};queue<Node> q;bool legal (int x,int y){return x>=0&&x<row&&y>=0&&y<col;}int  bfs(){while(!q.empty()){Node tmp = q.front();q.pop();if(tmp.x==row-1&&tmp.y==col-1)return tmp.d;for(int i=0;i<4;i++){int newx= tmp.x+dir[i][0];int newy= tmp.y+dir[i][1];if(legal(newx,newy)){int newk= board[newx][newy]?tmp.k-1:maxk;if(newk>=0&&vis[newx][newy][newk]==-1){vis[newx][newy][newk]=0;Node NewNode(newx,newy,tmp.d+1,newk);q.push(NewNode);}}}}return -1;}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int n;cin>>n;while(n--){cin>>row>>col>>maxk;memset(vis,-1,sizeof(vis));for(int i=0;i<row;i++){for(int j=0;j<col;j++){cin>>board[i][j];}}while(q.size())q.pop();Node begin(0,0,0,maxk);q.push(begin);int res=bfs();cout<<res<<endl;}return 0;} 

习题6-6

本题目的思路主要是要通过遍历,知道每个叶子节点的深度。具体思路见点击打开链接。通过学习别人的代码可以采用ios::sync_with_stdio(false);来提升读取速度。

可以使用auto自动推断类型的变量声明,来减少代码的输入量,但是auto在我的机器上测试的有问题。

我将他的代码进行了稍微的注释,以便大家学习。

#include<iostream>#include<string>#include<map>#include<algorithm>using namespace std;string line;map<long long,int> base;int sum;void dfs(int depth, int s,int e){if(line[s]=='['){int p=0;for(int i=s+1;i<e;i++){if(line[i]=='[') p++;if(line[i]==']') p--;if(p==0&&line[i]==',')//通过逗号,和括号标记P来进行判断自己所在的深度。 {dfs(depth+1,s+1,i-1);dfs(depth+1,i+1,e-1);}}}else {long long w=0;for(int i=s;i<=e;++i) w=w*10+line[i]-'0';//将字符转化为数字,并且是从高位从低位读取。 ++sum,++base[w<<depth];//并且记录总的节点数量。 //自己的值不变,整个天平能够达到的权重。 }}int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);ios::sync_with_stdio(false);int T;cin>>T;while(T--){cin>>line;base.clear();sum=0;dfs(0,0,line.size()-1);int maxn=0;for (map<long long,int>::iterator it = base.begin();it!=base.end();it++)maxn=max(maxn,it->second);//找到能够达到相同权重且值不变的节点数量。 cout<<sum-maxn<<endl;}} 


习题6-7

本题目理解好题意后,发现并没有涉及到图和树,对于树而言,我们可以看成是一个一对多的关系,图时一个多对多的关系。而对于题目中的一个trans,

可能有多个输入和输出,采用树或者图的方式处理显然不是太好。那么设置一个结构体存储trans。trans包含输出的place和需要从这个place,token的数量。

结构体中的数据采用in[place]=token数量的方式存储这些信息,再进行处理。

#include<iostream> #include<map>#include<cstring>using namespace std;const int maxn = 100;struct trans{int in[maxn],out[maxn];};int findtrans(trans T[],int token[],int NT,int NP){for (int i=0;i<NT;i++){int flag=0;for (int j=1;j<=NP;j++){if(token[j]<T[i].in[j]){flag=1;break;}}if(!flag)return i;}return -1;}int main(){ios::sync_with_stdio(false);//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);    int NP,NT,NF,token[maxn],rnd=1;trans T[maxn];while (cin>>NP&&NP){memset(token,0,sizeof(token));for(int i=1;i<=NP;i++){cin>>token[i];}cin>>NT;for(int i=0;i<NT;i++){for(int j=0;j<=NP;j++){T[i].in[j]=0;T[i].out[j]=0;}int tmp;while(cin>>tmp&&tmp){if(tmp<0){T[i].in[-tmp]++;}else{T[i].out[tmp]++;}}}cin>>NF;int flag=0,dead=0;for(int i=1;i<=NF;i++){int ex = findtrans(T,token,NT,NP);if (ex!=-1){for (int i=1;i<=NP;i++){if(T[ex].in[i]!=0)token[i]-=T[ex].in[i];if(T[ex].out[i]!=0)token[i]+=T[ex].out[i];}dead++;}else{flag=1;break;}}if(!flag){cout<<"Case "<<rnd++<<": still live after "<<NF<<" transitions"<<endl;}else{cout<<"Case "<<rnd++<<": dead after "<<dead<<" transitions"<<endl;}cout<<"Places with tokens:";for(int i=1;i<=NP;i++){if(token[i]>0)cout<<" "<<i<<" ("<< token[i] <<")";}cout<<endl<<endl;}return 0;}

习题6-8

本题就是一个DFS,主要递归要用的变量和结束的条件。输出格式相当烦人。

#include<iostream>#include<algorithm>#include<cmath>#include<cstring>using namespace std;const int maxn=1000;int G[maxn][maxn],res[maxn],lenres=0,route[maxn],routlen=0;int  allblack(int sr,int er,int sc,int ec){for(int i=sr;i<=er;i++){for(int j=sc;j<=ec;j++){if(G[i][j]!=1){return 0;}}}return 1;}void solve(int sr,int er,int sc,int ec,int depth,int index,int sum){int flag;if(sr>er||sc>ec){return ;}if(sr==er&&sc==ec){flag=allblack(sr,er,sc,ec);if(flag){sum=sum+index*pow(5,depth);res[lenres++]=sum;}return;}flag=allblack(sr,er,sc,ec);if(flag){sum=sum+index*pow(5,depth);res[lenres++]=sum;return;}else{int midr = sr+(er-sr)/2;int midc = sc+(ec-sc)/2;if(depth>=0)sum=sum+index*pow(5,depth);solve(sr,midr,sc,midc,depth+1,1,sum);solve(sr,midr,midc+1,ec,depth+1,2,sum);solve(midr+1,er,sc,midc,depth+1,3,sum);solve(midr+1,er,midc+1,ec,depth+1,4,sum);}}void locate(int &sr,int &er,int &sc,int &ec,int index){int midr = sr+(er-sr)/2;int midc = sc+(ec-sc)/2;if(index==1){er=midr;ec=midc;}if(index==2){er=midr;sc=midc+1;}if(index==3){sr=midr+1;ec=midc;}if(index==4){sr=midr+1;sc=midc+1;}}void fillone(int n){int sr=0,er=n-1,sc=0,ec=n-1; for (int i=0;i<routlen;i++){locate(sr,er,sc,ec,route[i]);}for(int i=sr;i<=er;i++)for(int j=sc;j<=ec;j++)G[i][j]=1; }int main(){//freopen("datain.txt","r",stdin);//freopen("dataout.txt","w",stdout);int n,rnd=1;while(cin>>n&&n){if(rnd!=1){cout<<endl;}cout<<"Image "<<rnd++<<endl;lenres=0;if(n>0){char c;c=getchar();for(int i=0;i<n;i++){for(int j=0;j<n;j++){c=getchar();G[i][j]=c-'0';}c=getchar();}solve(0,n-1,0,n-1,-1,0,0);sort(res,res+lenres);for(int i=0;i<lenres;i++){cout<<res[i];if((i+1)%12==0)cout<<endl; else if(i!=lenres-1)cout<<" ";}if(lenres%12!=0)cout<<endl;cout<<"Total number of black nodes = "<<lenres<<endl;}else{n=-n;memset(G,0,sizeof(G));int tmp;while(cin>>tmp&&tmp!=-1){memset(route,0,sizeof(route));routlen=0;while (tmp!=0){route[routlen++] = tmp%5;tmp = tmp/5;}fillone(n);}for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(G[i][j]==0)cout<<".";elsecout<<"*";}cout<<endl;}}}return 0;} 


阅读全文
0 0
原创粉丝点击