数据结构第四次上机实验
一、连通分量:
题意:求连通分量的个数
思路:我们已知一次DFS或BFS只能遍历一遍连通分支,我们可以利用这一点,通过统计DFS或BFS调用的次数来确定连通分量的个数。
代码:
1)、关键代码:
void graphl::DFS()
{
int *visited=new int[graphsize];
for(int k=1;k<=graphsize;k++)
visited[k]=0;
for(int k=1;k<=graphsize;k++)
{if(visited[k]==0)
{
DFS(k,visited);
sum++;
}
}
delete[]visited;
}
2)、完整代码
#include<iostream>
#include<queue>
#define max0 200000
const int INF=99999999;
int sum=0;
using namespace std;
struct edge{
friend class graphl;
int vadj;//节点编号
int cost;//权值
edge *link;
};
struct vertex
{
friend class graphl;
int vername;
edge *adj;
};
class graphl{
vertex* head;//顶点链表头指针
public:
int graphsize;//图中当前结点的个数
int dis[10000];//单源最短路的权值问题
int torder[10000];//
//graphl();
~graphl();
void BFS(const int s);
void DFS();
void DFS( const int v,int*visited);
int ctgraphl();
};
int graphl::ctgraphl()
{
int e,from,to,weight;
head=new vertex[max0];
cin>>graphsize;
for(int i=1;i<=graphsize;i++)
{
head[i].vername=i;
head[i].adj=NULL;
}
cin>>e;
if(e==0) {int x;cin>>x;return 0 ;}
else{
for(int i=1;i<=e;i++)
{
cin>>from>>to;
edge*p=new edge;
p->vadj=to;
p->cost=weight;
p->link=NULL;
edge*q=head[from].adj;
if(q==NULL)
head[from].adj=p;
else
{
while(q->link!=NULL)
q=q->link;
q->link=p;
}
p=new edge;
p->vadj=from;
p->link=NULL;
q=head[to].adj;
if(q==NULL)
head[to].adj=p;
else
{
while(q->link!=NULL)
q=q->link;
q->link=p;
}
}
}
return 1;
}
graphl::~graphl()
{
for(int i=1;i<=graphsize;i++)
{
edge*p=head[i].adj;
while(p!=NULL)
{
head[i].adj=p->link;
delete p;
p=head[i].adj;
}
}
delete []head;
}
void graphl::DFS()
{
int *visited=new int[graphsize];
for(int k=1;k<=graphsize;k++)
visited[k]=0;
for(int k=1;k<=graphsize;k++)
{if(visited[k]==0)
{
DFS(k,visited);
sum++;
}
}
delete[]visited;
}
void graphl::DFS( const int v,int*visited)
{
// cout<<v<<" ";
visited[v]=1;
edge*p=head[v].adj;
while(p!=NULL)
{
if(visited[p->vadj]!=1)
DFS(p->vadj,visited);
p=p->link;
}
}
void graphl::BFS(const int s)//从起始顶点开始广度优先遍历图
{
int *visited=new int [graphsize];
for(int k=1;k<=graphsize;k++)
{
visited[k]=0;
}
cout<<s<<" ";
visited[s]=1;
queue<int>q;
q.push(s);
while(!q.empty())
{
int v=q.front();
q.pop();
edge*p=head[v].adj;
while(p!=NULL)
{
if(visited[p->vadj]==0)
{
cout<<p->vadj<<" ";
visited[p->vadj]=1;
q.push(p->vadj);
}
p=p->link;
}
}
delete [] visited;
}
int main()
{
graphl A;
if(A.ctgraphl())
{A.DFS();
cout<<sum;
}
else
{
cout<<A.graphsize;
}
return 0;
}
二、整数拆分:
题意:求出不同的拆分方案,以及拆分方案的总数。
思路:这个题无论是方案求解还是去重都让我摸不着头脑,在学习了其他高手的代码后,了解到此类题应用DFS方法求解,并在生成节点以及时保持递增序列就可以去重。(字典序这一类的题都可以用此方法去重)。多次递归调用DFS是为了生成从以(1一直到k)为初始节点的拆分方案。
代码:
#include<iostream>
using namespace std;
#define max0 100000
int sum=0;//记录拆分的数
int n,k;//n为待拆分数,k为拆分的个数
int sur[100];//临时节点存储深搜时的拆分值
void dfs_interger_part(int su,int count);
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//给输入输出流解绑,使其达到scanf和printf的速度
cin>>n>>k;
for(int j=1;j<=n;j++)
{
sur[1]=j;
dfs_interger_part(n-j,2);
}
cout<<sum<<endl;
}
void dfs_interger_part(int su,int count)
{
if(su<0|| count>k+1) return ;//count值超过k+1后不再深搜
if(su==0&&count==k+1)
{
cout<<sur[1];
for(int i=2;i<=k;i++)
{
cout<<" "<<sur[i];
//printf(" %d",sur[i]);
}
cout<<endl;
sum++;
return ;
}
for(int i=sur[count-1];i<=su;i++)
{
sur[count]=i;
dfs_interger_part(su-i, count+1);
}
}
三、数字变换:
题意:根据规则(1)、(2)、(3)求出由x变为y的最少步数,以及每步的变换结果
思路:一般涉及求最优解的问题大部分可用BFS来做,因为BFS相当于层次遍历,逐层(逐步)逼近最优解。保存路径时我们保存的是该节点前一个节点的地址,我们利用栈将其正序输出。
代码:
1)、关键代码(广搜边搜索边形式生成结点入队):
void bfs_generate_node()
{
queue<node>q;
q.push(node(x,0));
dis[x]=1;
while(!q.empty())
{
node currentx=q.front();
q.pop();
if(currentx.varx==y)
{
cstep=currentx.step;
return ;
}
for(int i=1;i<=3;i++)
{ int v=pri(currentx.varx,i);
if(v<max0&&!dis[v])
{
dis[v]=1;
path[v]=currentx.varx;
q.push(node(v,currentx.step+1));
}
}
}
}
2)、输出路径:
int k=path[y];
stack<int >st;
while(path[k]!=-1)
{
st.push(k);
k=path[k];
}
cout<<cstep<<endl;
while(!st.empty())
{
printf("%d ",st.top());
st.pop();
}
printf("%d",y);
3)、完整代码
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
#define max0 200000
class node{
public:
int varx;
int step;
node(int x0,int y0):varx(x0),step(y0){}
};
int x,y;
int cstep;
int dis[200000];
int path[200000];
int pri(int x,int i);
void bfs_generate_node();
int main()
{
scanf("%d%d",&x,&y);
if(x==y) {cout<<0<<endl;return 0;}
else
{
for(int i=0;i<max0;i++)
path[i]=-1;
bfs_generate_node();
int k=path[y];
stack<int >st;
while(path[k]!=-1)
{
st.push(k);
k=path[k];
}
cout<<cstep<<endl;
while(!st.empty())
{
printf("%d ",st.top());
st.pop();
}
printf("%d",y);
return 0;
}
}
void bfs_generate_node()
{
queue<node>q;
q.push(node(x,0));
dis[x]=1;
while(!q.empty())
{
node currentx=q.front();
q.pop();
if(currentx.varx==y)
{
cstep=currentx.step;
return ;
}
for(int i=1;i<=3;i++)
{ int v=pri(currentx.varx,i);
if(v<max0&&!dis[v])
{
dis[v]=1;
path[v]=currentx.varx;
q.push(node(v,currentx.step+1));
}
}
}
}
int pri(int x,int i)
{
if(i==3)
return x-1;
else if(i==1)
return x+1;
else if(i==2)
return x*2;
}
四、旅行 1:
题意:求出给定城市到其他各城市的最小费用,并求出在最小费用下,到某城市经过最多城市数。
思路:总体为单源最短路问题,但题目中又加了一个条件,就是在最小费用下,尽可能多的多经过其他城市。此时我们只需要修改path[ ]数组即保存路径数组的更新条件即可。本题我采用了迪杰特斯拉的堆优化形式求解,该做法适用于边稀疏的图。
代码:
1)、关键代码(length函数是求该点到源点的路径长)---path[ ]数组更新代码
for(int i = 0;i<g[v].size();i++) {
//遍历所有后续边
edge e = g[v][i];
int to = e.to;
int cost = e.cost;
if(D[to] > D[v] + cost){
D[to] = D[v] + cost;
path[to]=v;
que.push(P(D[to],to));
}
else if(D[to]==D[v]+cost)
{
if(length(to)<length(v)+1){
path[to]=v;
que.push(P(D[to],to));
}
}
}
}
2)、完整代码
#include<bits/stdc++.h>//该算法适用于稀疏图
#define INF 100000005
#define MAX 100006
using namespace std;
typedef pair<int,int> P;
struct edge{
int to;
int cost;
edge(int t,int c):to(t),cost(c){
}
};
int length(int s0);
const int N = 100006;
vector<edge> g[N];
int dis[100][100],cd[100000]; //距离
int n,m; //n个点 m条边
//int path[100][100];//最小生成路的长度
int path[100000];
int D[100000];
void Dijkstra(int s){
static int countdij=0;
priority_queue<P,vector<P>,greater<P> > que; //小端优先队列
fill(D,D+100000,INF);//注意必须初始化为最大
D[s] = 0;
if(s==0)
for(int i=0;i<n;i++)
path[i]=-1;
que.push(P(0,s));
while(!que.empty()){
P p = que.top();
que.pop();
int v = p.second;
if(D[v] < p.first) continue; //说明该点无需重复
for(int i = 0;i<g[v].size();i++) {
//遍历所有后续边
edge e = g[v][i];
int to = e.to;
int cost = e.cost;
if(D[to] > D[v] + cost){
D[to] = D[v] + cost;
path[to]=v;
que.push(P(D[to],to));
}
else if(D[to]==D[v]+cost)
{
if(length(to)<length(v)+1){
path[to]=v;
que.push(P(D[to],to));
}
}
}
}
}
int main(){
// int trans;
int s=0;
cin>>n>>m;//m为边数,n为点数
cin>>s;
int a,b,d;
//Vector<edge> g[MAX]的初始化
for(int i = 0;i<MAX;i++) {
g[i].clear();
}
for(int i = 0;i<m;i++){
cin>>a>>b>>d;
g[a].push_back(edge(b,d));
g[b].push_back(edge(a,d));
}
Dijkstra(s);
int k;
// printf("\n%d -> %d 的最小路径长为:%d\n", s, t,D[t] );
// for(int i=0;i<n;i++)
// {for(int j=0;j<n;j++)
// cout<<dis[0][j]<<" ";
// cout<<"\n";
cout<<D[1];
for(int i=2;i<=n;i++)
cout<<" "<<D[i];
cout<<endl;
// for(int i=1;i<=n;i++)
// cout<<path[i]<<" "<<endl;
for(int i=1;i<=n;i++)
for(int k=i;path[k]!=0;k=path[k],cd[i]++);
cout<<cd[1];
for(int i=2;i<=n;i++)
cout<<" "<<cd[i];
return 0;
}
int length(int s0)
{
int s=0;
for(;path[s0]!=0;s0=path[s0])
{
s++;
}
return s;
}