2021-05-29

数据结构第四次上机实验

一、连通分量:

题意:求连通分量的个数

思路:我们已知一次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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值