JLU数据结构荣誉课——第三次上机实验
7-1 连通分量 (100 分)
题目分析
这道题用dfs或者bfs都行,每次dfs或者bfs能遍历一个联通分量,在遍历的时候计数就能解决。但当时我看到题首先想到了并查集 。可能是我对bfs和dfs的印象不深刻。
并查集呢,先将每个元素的所在集合的标志元素取出,进行排序后统计不同标记元素的个数,即为集合的个数,有多少个集合,就有多少个连通分量。
代码实现如下(并查集)
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
int a[100001];
int M[100001];
int Find(int x)
{
if(a[x]==x)return x;
return a[x]=Find(a[x]);
}
int main()
{
int i,n,m,t,z=1;
scanf("%d%d",&n,&m);
for(i=0;i<=n;i++)
a[i]=i;
for(i=0;i<m;i++)
{
int p,q;
scanf("%d%d",&p,&q);
if(Find(p)!=Find(q))
{
int b=Find(q),c=Find(p);
a[c]=b;
}
}
for(i=1;i<=n;i++)
{
M[i]=Find(i);
}
sort(M+1,M+n+1);
for(i=1;i<n;i++)
{
if(M[i]!=M[i+1])z++;
}
printf("%d",z);
}
7-2 整数拆分 (100 分)
题目分析
主要是考察dfs回溯法的运用,在dfs过程中注意维护序列的不减,同时结合剩余量以及现有元素的个数进行判断。
函数int Dfs(int re,int lentgh)传入当前剩余量,以及当前元素个数。而使用
if(lentgh==m&&re!=0)return 0;
if(lentgh!=m&&re==0)return 0;
分别判断长度为m但还有剩余和长度不满m但没有剩余的情况,如果出现这两种情况就进行回溯。
代码实现如下
#include<bits/stdc++.h>
using namespace std;
int answer[100];
int n,m;
int t;
int Dfs(int re,int lentgh)
{
if(lentgh==m&&re!=0)return 0;
if(lentgh!=m&&re==0)return 0;
if(lentgh==m&&re==0)
{
printf("%d",answer[0]);
for(int i=1;i<lentgh;i++)
printf(" %d",answer[i]);
printf("\n");
t++;
return 0;
}
for(int i=answer[lentgh-1];i<=re;i++)
{
answer[lentgh]=i;
Dfs(re-i,lentgh+1);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n/m;i++)//第一位不能大于n/m取地板
{
answer[0]=i;
Dfs(n-i,1);
}
cout<<t;
}
7-3 数字变换 (100 分)
题目分析
因为是求最短的路径,应该使用bfs实现,我是用的队列。
使用结构体数组q记录第Q个有效(第一次出现)的节点的信息,包括step:深度(到这一步的操作数),father:他的上一个数的下标,num:他的值,以及n:他的下标(这个有一点多余)。最后通过father和num一步步获得操作路径。
考虑到会有很多重复入队的情况,引入了visited数组记录已经入队的元素,如果该元素已经被标记那么就不对该元素进行任何处理。如果不考虑重复入队的问题,空间会爆掉为什么呢,因为队列所需的空间是呈指数级增长的,而且底数为3,大概说来,就是3的 操作数 的次方级别的空间。
另外,还有对于x>=y这种特殊情况要给予考虑(当然,>和=是两种不同的情况)
具体代码实现
#include<bits/stdc++.h>
using namespace std;
struct Node
{
int step;
int num;
int father;
int n;
};
queue<Node>stk;
int mm[100005];
int visited[200001];
Node q[100005];
int main()
{
int a,b,Q=1;
scanf("%d%d",&a,&b);
if(b==a)
{
printf("0");
return 0;
}
if(b<a)
{
printf("%d\n%d",a-b,a-1);
for(int i=a-2;i>=b;i--)
printf(" %d",i);
return 0;
}
Node *x,y;
y.step=0;
y.num=a;
y.n=0;
visited[a]=0;
stk.push(y);
while(1)
{
x=&stk.front();
stk.pop();
if(x->num==b&&Q!=1)break;
if(Q<100000)
{
q[Q].step=x->step+1;
q[Q].num=x->num+1;
q[Q].father=x->n;
q[Q].n=Q;
if(q[Q].num>=0)
if(visited[q[Q].num]==0&&q[Q].num<=b+100)
{
visited[q[Q].num]=1;
stk.push(q[Q++]);
}
q[Q].step=x->step+1;
q[Q].num=x->num*2;
q[Q].father=x->n;
q[Q].n=Q;
if(q[Q].num>=0)
if(visited[q[Q].num]==0&&q[Q].num<=b+100)
{
visited[q[Q].num]=1;
stk.push(q[Q++]);
}
q[Q].step=x->step+1;
q[Q].num=x->num-1;
q[Q].father=x->n;
q[Q].n=Q;
if(q[Q].num>=0)
if(visited[q[Q].num]==0&&q[Q].num<=b+100)
{
visited[q[Q].num]=1;
stk.push(q[Q++]);
}
}
}
int i,nn=x->step,j;
j=x->n;
for(i=0;i<nn;i++)
{
mm[i]=q[j].num;
j=q[j].father;
}
printf("%d\n%d",nn,mm[nn-1]);
for(i=nn-2;i>=0;i--)
printf(" %d",mm[i]);
}
7-4 旅行 I (100 分)
题目分析
这是典型的最短路问题,本来打算先试一试时间复杂度为O(n^2)的Dijkstra算法,如果被卡时间了,就换时间复杂度为O(kE)的spfa,但这道题老师没有卡这个点。
需要注意的是,题目中有提到,在代价最小时要尽可能走更长的路,对于这一点,我引进了path数组,记录每个节点深度,在进行松弛操作时,如果权值相同,就取长度更长的路。
具体代码实现
#include<bits/stdc++.h>
using namespace std;
struct Node
{
int name;
int cost;
};
#define Max 10003
vector<Node> Map[Max];
int visited[Max];
long long int cost[Max];
int path[Max];
int main()
{
int n,m,s,i,j,u;
cin>>n>>m>>s;
Node a;
for(i=1;i<=n;i++)
cost[i]=1000000;
for(i=0;i<m;i++)
{
Node a;
int x,y,t;
cin>>x>>y>>t;
a.name=x;
a.cost=t;
Map[y].push_back(a);
a.name=y;
Map[x].push_back(a);
}
int mincost;
visited[s]=0;
cost[s]=0;
path[s]=0;
for(j=1;j<=n;j++)
{
mincost=100000;
for(i=1;i<=n;i++)
{
if(cost[i]<=mincost&&visited[i]==0)
{
u=i;
mincost=cost[i];
}
}
visited[u]=1;
for(vector<Node>::iterator k = Map[u].begin();k!=Map[u].end();k++)
{
if(cost[k->name]>cost[u]+k->cost||cost[k->name]==cost[u]+k->cost&&path[k->name]<path[u]+1)
{
cost[k->name]=cost[u]+k->cost;
path[k->name]=path[u]+1;
}
}
}
for(int i=1;i<=n;i++)
{
printf("%d",cost[i]);
if(i!=n)printf(" ");
}
printf("\n");
for(int i=1;i<=n;i++)
{
printf("%d",path[i]);
if(i!=n)printf(" ");
}
}