例题6-1(TLE/WA)
本题出现TLE的情况,因为在本题,我使用了太多的STL。在结果方面估计也会有些问题,因为对于lock/unlock的机制理解不清楚,现提出自己的错误代码。
关于双端队列的用法可以参看点击打开链接
#include
#include
#include
#include
#include
#include
#include
例题6-2
本题不多说,书中的分析和解法简洁明了。
#include
#include
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>fir&&fir)
{
int dc[maxn];
stack st;
dc[0]=fir;
for(int i=1;i>dc[i];
}
int a=0,c=0,flag=1;
while(c
例题6-3
本题我采用map存储各个字母矩阵对应的行和列,而本题使用结构体来存行和列,用数字索引代替字母,具有一定的技巧性,而在中间过程的计算中,也直接不存中间过程的名字,只存储计算中间过程中的行和列。而我在中间过程中,将中间过程存储在map中。
#include
#include
#include
例题6-4
本题目的写法非常经典,技巧很多,建议逐步运行实现。实现链表的方式,和使用指针实现的不一样,但是效率更高。实际上,思想就是在插入某个元素的时候,要保证上一个元素指向自己,而自己要指向下一个元素(可能为0)。实际上,实现了在上一个元素和光标之间的插入操作。
#include
#include
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
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;
//初始化boxs
boxs[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 "<
例题6-6
本题目见书中分析,书中给出两段代码,第一段TLE,第二段AC,但是第一段非常容易理解,第一段可以理解为穷举。
第一段:TLE
#include
#include
const int maxn= 20 ;
int s[1<n) break; //循环结束条件。
}
}
printf("%d\n",k/2);
}
}
return 0;
}
第二段:AC
#include
#include
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
例题6-7
本题就是典型的如果构建一个树,构建后进行宽度遍历(层次遍历)。主要注意对输入的处理方式,利用队列来进行树的宽度遍历。本题使用指针相对而言,比较好理解。#include
#include
#include
#include
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;ileft==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 &ans)
{
queue 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 ans;
if(failed)
{
printf("not complete");
}
else if(bfs(ans))
{
for(vector::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 freenodes;
Node node[maxn];
void init()
{
for(int i=0;ileft=u->right=NULL;
u->have_value=false;
return u;
}
void deletenode(Node* u)
{
freenodes.push(u);
}
//实际上,如果没有特殊要求,使用new动态申请空间,使用delete释放空间,就如同最开始代码中生成和删除节点的方法。
例题6-8
本题先通过后序遍历和中序遍历构建二叉树,在通过深度优先遍历,从根到叶子搜索每一条路径,然后选取最小权的路径。数据结构采用的为两个数字lch和rch来存储根的左右子树,构造树和深度优先遍历都采用了递归的手段。下面的代码为书中代码,我补加了注释,便于大家理解。
#include
#include
#include
#include
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
例题6-9
一开始对题目理解的不够完全,输出"YES"的条件为各个子天平都要平衡,而天平的权值为子天平权值的和。肯定是要采用递归的模式来进行。我的代码如下,比较好理解。但书中的代码更加精髓,我对书中代码进行了注释,便于理解学习。
我的代码:
#include
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"<
首先,使用递归必须明确递归结束的条件,显然题目中结束的条件为W1不为0,则结束左子树的递归,当W2不为0,则结束右子树的递归。而我们感兴趣的是子树是不是平衡的和子树的权值总和,这样才能计算自己是否平衡,所以,书中代码使用b1和b2来确定左右子树是否平衡,使用W1*D1==W2*D2来确定自己是否平衡,而权值则通过引用进行传递W为权值和,W1为左子树的权值,W2为右子树的权值。
#include
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
#include
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);
}
else
sumres[1]=rootvalue;
cout<<"Case "<>rootvalue;
}
return 0;
}
例题6-11
本题一开始真不知道怎么做,所以借鉴了书中代码。我给书中的代码写了少许注释,以便于理解。
#include
#include
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);//1
draw(s,p,r,c,w/2);//2
draw(s,p,r+w/2,c,w/2);//3
draw(s,p,r+w/2,c+w/2,w/2);//4
}else if(ch=='f')
{
for(int i=r;i
例题6-12
本题目主要是对八连块的理解,八连块和连通的数量没关系,只是表示可以上下左右斜上斜下等8个方向进行移动。通过dfs进行遍历,具体代码注释已经写好,可以参考学习。
#include
#include
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
例题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
#include
#include
#include
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>input[i];
int tmp,bi[4];
m=0;
for(int j=0;j=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=0;m--)
{
if(idx[m][j]>0)
{
up=idx[m][j];
break;
}
}
//向下搜索
for(int m=i;m0)
{
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;m0)
{
right=idx[i][m];
break;
}
}
//如果上下左右都相等的话,为空点。
//否则不为空洞点。
if(!(left==right&&left==up&&up==down&&left!=-1))
{
dfsz(i,j,-1);
}
}
}
}
// for(int i=0;i
例题6-14
本题目将书中的代码补全,并进行了注释。书中进行判断是否前进的inside()函数,我不知道其中的含义,也没有写这个函数,UVa还是通过了。这里其实要注意的就是两个点1是存从初始状态到某个点的最短路长度,2是存BFS中的父节点。实际上,在进行BFS采用队列进行辅助。
#include
#include
#include
#include
#include
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 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 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<>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
#include
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
例题6-16
通过存在欧拉回路的条件进行判断,再使用dfs来判断是否连通。
#include
#include
#include
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>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."<
例题6-17
本题目的难点还是在对输入数据的处理上,难点并不在数据结构,书中的代码也易于理解。
#include
#include
#include
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=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
例题6-20
本题书中数相当重要使用了两次BFS,具体详细的分析和解释可以参考。
点击打开链接
写的很详细。我将他的代码贴在下面。另外本题采用BFS采用邻接表的形式,数据结构中可以使用邻接表和邻接矩阵存图,具体将会在第六章的总结中呈现。
#include
#include
#include
#include
using namespace std; //min()函数
#define max 100000
#define inf 0x7fffffff
typedef 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 edge[max];//邻接表记录图
void bfs(int start,int end){
memset(inqueue,0,n);
memset(vis,0,n);
int u,v,c;
queue 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
例题6-1
本题就是一个栈的使用,一定要注意输入的格式问题。
#include
#include
#include
using namespace std;
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int n;
cin>>n;
getchar();
while(n--)
{
stack sc;
while(sc.size()) sc.pop();
string s;
getline(cin,s);
bool flag = true;
for(int i=0;i
习题6-2
本题第一眼看是树,实际上和树关系不大,根据题意,我们可以知道在叶子节点分别可以编号成十进制的0-2^n,n为树深度,那么再根据输入的编码和树中给出的顺序进行一一对应,这样就输入的编码转化为10进制,直接就能得出答案。
#include
#include
#include
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 #"<>s;
tim[s[1]-'0']=i;
}
for(int i=0;i< 1<>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<
习题6-3
可以参照例题6-10进行,对于树而言,三种遍历方式非常重要,我也将会在第六章进行总结。
#include
#include
#include
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<
习题6-4
本题目使用一次BFS就行。难度不大。为了使用队列方便,除了使用(x,y)坐标表示一个格子外,也将棋盘的左下角开始从左到右,从下到上由0-63进行编号。
#include
#include
#include
#include
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 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 "<
习题6-5
本题目采用BFS,但是不仅要考虑到达每个点的最短距离,还要考虑到到达这个点的时候,剩余的可以连续穿越障碍的步数。这样到达可能出现到达某点的时候,离起点的距离不同,剩余可以连续跨越障碍的不通,但是根据BFS的运行规则,第一个到达终点的肯定就是最短路径的。程序如下,注意队列的清空。
#include
#include
#include
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 q;
bool legal (int x,int y)
{
return x>=0&&x=0&&y =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>board[i][j];
}
}
while(q.size())q.pop();
Node begin(0,0,0,maxk);
q.push(begin);
int res=bfs();
cout<
习题6-6
本题目的思路主要是要通过遍历,知道每个叶子节点的深度。具体思路见点击打开链接。通过学习别人的代码可以采用ios::sync_with_stdio(false);来提升读取速度。
可以使用auto自动推断类型的变量声明,来减少代码的输入量,但是auto在我的机器上测试的有问题。
我将他的代码进行了稍微的注释,以便大家学习。
#include
#include
#include
习题6-7
本题目理解好题意后,发现并没有涉及到图和树,对于树而言,我们可以看成是一个一对多的关系,图时一个多对多的关系。而对于题目中的一个trans,
可能有多个输入和输出,采用树或者图的方式处理显然不是太好。那么设置一个结构体存储trans。trans包含输出的place和需要从这个place,token的数量。
结构体中的数据采用in[place]=token数量的方式存储这些信息,再进行处理。
#include
#include
习题6-8
本题就是一个DFS,主要递归要用的变量和结束的条件。输出格式相当烦人。
#include
#include
#include
#include
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>n&&n)
{
if(rnd!=1)
{
cout<0)
{
char c;
c=getchar();
for(int i=0;i