第三节暑期信息奥赛课——图论

2019-07-08 16:02:00

第三节暑期信息奥赛课——图论

绪言

图论这东西,纠缠我很久啦!我接触的第一道图论题是2017普及组D1T3 洛谷P3956 棋盘(dijkstra算法)

当时用暴力法,整了个不到50分,但是还是不会这个算法。

图论乃是NOIP的重点之一!


 最短路径问题


----Floyd

时间复杂度O(n^3)

只有五行!!!死也要给我背下来!!!

#include<iostream>
//#include<cstring>
using namespace std;
int main(){
    int num,way;
    cin>>num>>way;
    int sz[num+1][num+1];
    int tx,ty,tz;
    for(int n=1;n<=num;n++)
    for(int m=1;m<=num;m++)
        sz[n][m]=0xFFF;
    //memset(sz,0x3F,sizeof(sz);
    for(int n=1;n<=way;n++)
    {
        cin>>tx>>ty>>tz;
        sz[tx][ty]=min(sz[tx][ty],tz);//陷阱一:可能有重复输入两点距离 
    }
    
    for(int i=1;i<=num;i++)
        sz[i][i]=0;//自己到自己的距离是0 
    //核心代码 
    for(int k=1;k<=num;k++){//k一定是在最外层,在动规中是状态 
        for(int i=1;i<=num;i++)
        for(int j=1;j<=num;j++){
        if(sz[i][j] > sz[i][k]+sz[k][j]);//寻找桥梁点k,会使得i to k&k to j比i to j更短 
            sz[i][j] = sz[i][k]+sz[k][j];
        }
    }
    
    for(int i=0;i<num;i++){
        for(int j=0;j<num;j++)
            cout<<sz[i][j]<<" ";
        cout<<endl;
        }
    return 0;
} 

----Dijkstra

基础版的O(n^2),代码稍长

可优化

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=0x3F,inf=0x3F3F3F;
int num,way,minNum,minIndex;
int sz[MAXN][MAXN];
int dis[MAXN];
bool vis[MAXN];
void dij(int s){
    memset(dis,1,sizeof(dis));
    memset(vis,1,sizeof(vis));
    for(int p=1;p<=num;p++)
        dis[p]=sz[s][p];
    vis[s]=1;
    
    for(int j=1;j<=num;j++){
        int minIndex=1,minNum=inf+1;
        for(int i=1;i<=num;i++)
            if(vis[i]==1 && dis[i]<minNum){
                minNum=dis[i];
                minIndex=i;
            }
    } 
    
    vis[minIndex]=1;
    for(int i=1;i<=num;i++)
        if(vis[i]==1 && dis[i]>dis[minIndex]+sz[minNum][i]){
            dis[i]==dis[minIndex]+sz[minNum][i];
        }
}
int main(){
    cin>>num>>way;
    int tx,ty,tz;
    //初始化 
    for(int n=1;n<=num;n++)
    for(int m=1;m<=num;m++)
        sz[n][m]=MAXN;
    //msmset(sz,0x3F,sizeof(sz);
    for(int n=1;n<=way;n++)
    {
        cin>>tx>>ty>>tz;
        sz[tx][ty]=min(sz[tx][ty],tz);//陷阱一:可能有重复输入两点距离 
    }
    dij(1);
    for(int i=1;i<=num;i++){
        for(int j=1;j<=num;j++)
            cout<<sz[i][j]<<" ";
        cout<<endl;
        }
    return 0;
} 
/*
1、把sz[1][j]搬到一个一维数组dis中(起点到其他所有点的距离) 
2、松弛操作:从dis中找一个没用过,且最小的点
3、 
*/

最小生成树问题


----kruskal

要用到并查集哟(^U^)ノ~YO

#include<iostream>
using namespace std;
int n,m;
const int MAXN=0x3F3F3F;
int f[MAXN];
//并查集开始 
int init(){//初始化 
    for(int i=1;i<=n;i++)
        f[i]=i;
} 

int getf(int i){
    if(i==f[i]) return i;//没有祖先,或者说就是它自己 
    else return f[i]=getf(f[i]);//有祖先,就把f[i]也变成即将查找到的它的祖先的祖先,状态压缩 
}

void merge(int a,int b ){
    int x=getf(a);
    int y=getf(b);
    
    if(x!=y) f[x]=f[y];
}    
int main(){
    int a,b,c;
    cin>>n>>m;
    init(); //初始化
    for(int i=1;i<=m;i++)
        cin>>a>>f[a];
    
        
    return 0;
} 
 
/*
kruskal算法 
1、清除所有边
2、找两点使得距离最短,连线
3、用并查集判断有没有环(回路)
4、 
*/ 
/*
并查集
对一个集合进行合并,查找
1、把一个一维数组sz的值初始化为其下标
2、若A与B有关系,则sz[A]=B;
3、若A与C有关系 
最后,有多少个下标仍是值的数值,就有 
*/ 
kruskal

代码的输入有问题,记得修复!


----Prim

(未完待续......)

以后做到了再说吧。。。

原文地址:https://www.cnblogs.com/send-off-a-friend/p/11138181.html