这次渣诚来写解题报告,我只放我的标程;大量与书中风格不同的代码请慎入。
A. 二叉树的最短路
利用二叉树的数学性质。
#include<cstdio>
using namespace std;
int main()
{
int n,x,y;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&x,&y);
int ans=0;
while(x!=y)
{
x>y?x>>=1:y>>=1;
++ans;
}
printf("%d\n",ans);
}
}
B. 同构二叉树
配套的学习指导上有源代码。
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int MAXN=1005;
struct BTNode
{
char data[10];
BTNode *lchild,*rchild;
};
char str[MAXN];
void CreateBTNode(BTNode *&b)
{
BTNode *St[MAXN],*p;
int top=-1,k,l=strlen(str);
b=NULL;
for(int i=0; i<l; ++i)
switch(str[i])
{
case '(':
St[++top]=p;
k=1;
break;
case ')':
--top;
break;
case ',':
k=2;
break;
default:
p=(BTNode *)malloc(sizeof(BTNode));
int j=0;
while(i<l&&str[i]!='('&&str[i]!=')'&&str[i]!=',')
p->data[j++]=str[i++];
--i;
p->data[j]='\0';
p->lchild=p->rchild=NULL;
if(!b)
b=p;
else
switch(k)
{
case 1:
St[top]->lchild=p;
break;
case 2:
St[top]->rchild=p;
break;
}
}
}
bool Symm(BTNode *b1,BTNode *b2)
{
if(!b1&&!b2)
return true;
if(!b1||!b2)
return false;
else
return Symm(b1->lchild,b2->lchild)&&Symm(b1->rchild,b2->rchild);
}
bool Symmtree(BTNode *b)
{
if(!b)
return true;
return Symm(b->lchild,b->rchild);
}
void DestroyBT(BTNode *&b)
{
if(b->lchild)
DestroyBT(b->lchild);
if(b->rchild)
DestroyBT(b->rchild);
free(b);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",str);
BTNode *b;
CreateBTNode(b);
puts(Symmtree(b)?"YES":"NO");
DestroyBT(b);
}
}
C. Papers
哈夫曼树,扔板子就行了。其实不需要建一棵树出来,哈夫曼树的本质是贪心,故而可以建一个堆或用一个优先队列来搞。我写了建哈夫曼树和优先队列两个版本。
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=5005;
const int INF=0x3f3f3f3f;
struct HuffmanTree
{
int parent[MAXN<<1];
int lchild[MAXN<<1];
int rchild[MAXN<<1];
int weight[MAXN<<1];
} ht;
int n;
void CreateHT()
{
int lnode,rnode,min1,min2;
memset(ht.parent,0xff,sizeof(ht.parent));
memset(ht.lchild,0xff,sizeof(ht.lchild));
memset(ht.rchild,0xff,sizeof(ht.rchild));
for(int i=n; i<(n<<1)-1; ++i)
{
min1=min2=INF;
lnode=rnode=-1;
for(int j=0; j<i; ++j)
if(!~ht.parent[j])
{
if(ht.weight[j]<min1)
{
min2=min1;
rnode=lnode;
min1=ht.weight[j];
lnode=j;
}
else if(ht.weight[j]<min2)
{
min2=ht.weight[j];
rnode=j;
}
}
ht.weight[i]=ht.weight[lnode]+ht.weight[rnode];
ht.lchild[i]=lnode;
ht.rchild[i]=rnode;
ht.parent[lnode]=ht.parent[rnode]=i;
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0; i<n; ++i)
scanf("%d",&ht.weight[i]);
CreateHT();
int ans=0;
for(int i=n; i<(n<<1)-1; ++i)
ans+=ht.weight[i];
printf("%d\n",ans);
}
}
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
int main()
{
int n,x;
while(~scanf("%d",&n))
{
priority_queue<int,vector<int>,greater<int> > pq;
while(n--)
{
scanf("%d",&x);
pq.push(x);
}
int ans=0;
while(pq.size()>1)
{
int x=pq.top();
pq.pop();
int y=pq.top();
pq.pop();
ans+=x+y;
pq.push(x+y);
}
printf("%d\n",ans);
}
}
D. 电报编码
哈夫曼编码,板子题。还是建哈夫曼树和优先队列两个版本。
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=30;
const int MAXM=2005;
const int INF=0x3f3f3f3f;
struct HuffmanTree
{
int parent[MAXN<<1];
int lchild[MAXN<<1];
int rchild[MAXN<<1];
int weight[MAXN<<1];
} ht;
char str[MAXM];
int cnt[MAXN],n;
void CreateHT()
{
int lnode,rnode,min1,min2;
memset(ht.parent,0xff,sizeof(ht.parent));
memset(ht.lchild,0xff,sizeof(ht.lchild));
memset(ht.rchild,0xff,sizeof(ht.rchild));
for(int i=n; i<(n<<1)-1; ++i)
{
min1=min2=INF;
lnode=rnode=-1;
for(int j=0; j<i; ++j)
if(!~ht.parent[j])
{
if(ht.weight[j]<min1)
{
min2=min1;
rnode=lnode;
min1=ht.weight[j];
lnode=j;
}
else if(ht.weight[j]<min2)
{
min2=ht.weight[j];
rnode=j;
}
}
ht.weight[i]=ht.weight[lnode]+ht.weight[rnode];
ht.lchild[i]=lnode;
ht.rchild[i]=rnode;
ht.parent[lnode]=ht.parent[rnode]=i;
}
}
int main()
{
while(~scanf("%d",&n))
{
scanf("%s",str);
int l=strlen(str);
n=0;
memset(cnt,0,sizeof(cnt));
for(int i=0; i<l; ++i)
++cnt[str[i]-'a'];
for(int i=0; i<26; ++i)
if(cnt[i]>0)
ht.weight[n++]=cnt[i];
CreateHT();
int ans=0;
for(int i=n; i<(n<<1)-1; ++i)
ans+=ht.weight[i];
printf("%d\n",ans);
}
}
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int MAXN=2005;
char str[MAXN];
int main()
{
int n,cnt[30];
while(~scanf("%d",&n))
{
scanf("%s",str);
int l=strlen(str);
memset(cnt,0,sizeof(cnt));
for(int i=0; i<l; ++i)
++cnt[str[i]-'a'];
priority_queue<int,vector<int>,greater<int> > pq;
for(int i=0; i<26; ++i)
if(cnt[i]>0)
pq.push(cnt[i]);
int ans=0;
while(pq.size()>1)
{
int x=pq.top();
pq.pop();
int y=pq.top();
pq.pop();
ans+=x+y;
pq.push(x+y);
}
printf("%d\n",ans);
}
}
E. Who Are My Friends?(Ⅰ)
原意是考查并查集,不断合并朋友,最后看还剩下几组。事实上也可以用图论做,认为朋友之间存在一条边,则连通子图的个数就是答案,遍历一遍即可。
这里想说一点是,书上对并查集的优化是根据树的层次合并以维护树高,保持每次查询的复杂度在O(logn);但最常用的优化方式是路径压缩,可以使查询复杂度接近O(1);实测时发现数据量小的时候几无差别,数据量大的时候路径压缩会更快一些。我这里放出三种标程,分别是路径压缩的并查集,维护树高的并查集,以及图论dfs。
#include<cstdio>
using namespace std;
const int MAXN=10005;
int u[MAXN];
void init()
{
for(int i=0; i<MAXN; ++i)
u[i]=i;
}
int find(int x)
{
if(u[x]!=x)
u[x]=find(u[x]);
return u[x];
}
void merge(int x,int y)
{
u[find(x)]=find(y);
}
int main()
{
int n,m,a,b;
while(~scanf("%d%d",&n,&m))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
merge(a,b);
}
int ans=0;
for(int i=1; i<=n; ++i)
if(find(i)==i)
++ans;
printf("%d\n",ans);
}
}
#include<cstdio>
using namespace std;
const int MAXN=10005;
int u[MAXN],r[MAXN];
void init()
{
for(int i=0; i<MAXN; ++i)
{
u[i]=i;
r[i]=0;
}
}
int find(int x)
{
if(u[x]!=x)
return find(u[x]);
return u[x];
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(r[x]>r[y])
u[y]=x;
else
{
u[x]=y;
if(r[x]==r[y])
++r[y];
}
}
int main()
{
int n,m,a,b;
while(~scanf("%d%d",&n,&m))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
merge(a,b);
}
int ans=0;
for(int i=1; i<=n; ++i)
if(find(i)==i)
++ans;
printf("%d\n",ans);
}
}
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=10005;
const int MAXM=200005;
struct graph
{
int head[MAXN];
int to[MAXM];
int next[MAXM];
int tot;
void init()
{
tot=0;
memset(head,0xff,sizeof(head));
}
void add(int x,int y)
{
to[tot]=y;
next[tot]=head[x];
head[x]=tot++;
}
} g;
bool vis[MAXN];
void dfs(int x)
{
vis[x]=true;
for(int i=g.head[x]; ~i; i=g.next[i])
{
int v=g.to[i];
if(!vis[v])
dfs(v);
}
}
int main()
{
int n,m,a,b;
while(~scanf("%d%d",&n,&m))
{
g.init();
memset(vis,false,sizeof(vis));
while(m--)
{
scanf("%d%d",&a,&b);
g.add(a,b);
g.add(b,a);
}
int ans=0;
for(int i=1; i<=n; ++i)
if(!vis[i])
{
++ans;
dfs(i);
}
printf("%d\n",ans);
}
}
F. Who Are My Friends?(Ⅱ)
跟上一题只有很小的区别。并查集合并,每次判断两个人是否在一个组。也可以用图论做,每次判断两个人是否在一个连通子图,方法是遍历时记录每个点所在连通子图的标号。还是三种姿势的代码。
#include<cstdio>
using namespace std;
const int MAXN=10005;
int u[MAXN];
void init()
{
for(int i=0; i<MAXN; ++i)
u[i]=i;
}
int find(int x)
{
if(u[x]!=x)
u[x]=find(u[x]);
return u[x];
}
void merge(int x,int y)
{
u[find(x)]=find(y);
}
bool query(int x,int y)
{
return find(x)==find(y);
}
int main()
{
int n,m,k,a,b;
while(~scanf("%d%d%d",&n,&m,&k))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
merge(a,b);
}
while(k--)
{
scanf("%d%d",&a,&b);
puts(query(a,b)?"Great!":"Pity...");
}
}
}
#include<cstdio>
using namespace std;
const int MAXN=10005;
int u[MAXN],r[MAXN];
void init()
{
for(int i=0; i<MAXN; ++i)
{
u[i]=i;
r[i]=0;
}
}
int find(int x)
{
if(u[x]!=x)
return find(u[x]);
return u[x];
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(r[x]>r[y])
u[y]=x;
else
{
u[x]=y;
if(r[x]==r[y])
++r[y];
}
}
bool query(int x,int y)
{
return find(x)==find(y);
}
int main()
{
int n,m,k,a,b;
while(~scanf("%d%d%d",&n,&m,&k))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
merge(a,b);
}
while(k--)
{
scanf("%d%d",&a,&b);
puts(query(a,b)?"Great!":"Pity...");
}
}
}
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=10005;
const int MAXM=200005;
struct graph
{
int head[MAXN];
int to[MAXM];
int next[MAXM];
int tot;
void init()
{
tot=0;
memset(head,0xff,sizeof(head));
}
void add(int x,int y)
{
to[tot]=y;
next[tot]=head[x];
head[x]=tot++;
}
} g;
int u[MAXN],cnt;
bool vis[MAXN];
void dfs(int x)
{
u[x]=cnt;
vis[x]=true;
for(int i=g.head[x]; ~i; i=g.next[i])
{
int v=g.to[i];
if(!vis[v])
dfs(v);
}
}
int main()
{
int n,m,k,a,b;
while(~scanf("%d%d%d",&n,&m,&k))
{
g.init();
memset(vis,false,sizeof(vis));
while(m--)
{
scanf("%d%d",&a,&b);
g.add(a,b);
g.add(b,a);
}
cnt=0;
for(int i=1; i<=n; ++i)
if(!vis[i])
{
++cnt;
dfs(i);
}
while(k--)
{
scanf("%d%d",&a,&b);
puts(u[a]==u[b]?"Great!":"Pity...");
}
}
}
G. 邻接矩阵转邻接表
不能更水,根据两种结构的联系来做,不用建图。
#include<cstdio>
using namespace std;
int main()
{
int m,n,x;
while(~scanf("%d%d",&m,&n))
for(int i=0; i<m; ++i)
{
printf("%d:",i);
for(int j=0; j<m; ++j)
{
scanf("%d",&x);
if(x>0)
printf("%d ",j);
}
putchar('\n');
}
}
因为题出得很水,所以这次上机结果看起来相当好……