图论算法

图论算法

1. 最小生成树(Kruscal算法)

/**** **** **** **** **** ****

  • Function Name : 最小生成树(Kruscal算法)
  • Description : ZJU 1203
    **** **** **** **** **** ****/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
struct struct_edges
{
     int bv,tv; //bv 起点  tv 终点
     double w; //权值
};
struct_edges edges[10100]; //边集
struct struct_a
{
     double x;
     double y;
};
struct_a arr_xy[101];
int point[101],n,e;  //n 顶点数, e 边数(注意是无向网络)
double sum;

int kruscal_f1(int point[], int v)  
{
     int i = v;
     while(point[i] > 0)     i = point[i];
     return i;
}

bool UDlesser(struct_edges a, struct_edges b)
{return a.w < b.w;}

void kruscal() //只需要准备好n,e,递增的边集edges[]即可使用
{
     int v1,v2,i,j;
     for(i=0; i<n ;i++)     point[i]=0;
     i = j = 0;
     while(j<n-1 && i<e) {
          v1 = kruscal_f1(point, edges[i].bv);
          v2 = kruscal_f1(point, edges[i].tv);
          if(v1 != v2) {
               sum += edges[i].w; //注意sum初始为0
               point[v1]=v2;
               j++;
          }
          i++;
     }
}

int main()
{
     int k,i,j;
     cin>>n;
     k=0;
     while(n != 0) {
          sum=0;
          k++;
          for(i=0; i<n ;i++)
               cin>>arr_xy[i].x>>arr_xy[i].y;
          e=0;
          for(i=0; i<n ;i++) //从0开始计数
               for(j=i+1; j<n ;j++) //注意是无向网络
               {
                    if(i == j) continue;  
                    edges[e].bv=i;
                    edges[e].tv=j;
                    edges[e].w=sqrt((arr_xy[i].x-arr_xy[j].x)*(arr_xy[i].x-arr_xy[j].x)+(arr_xy[i].y-arr_xy[j].y)*(arr_xy[i].y-arr_xy[j].y));
                    e++;
               }
               sort(edges,edges+e,UDlesser);  //得到一个递增的边集,注意是从0开始计数
               kruscal();
               
               printf("Case #%d:\n",k);  //cout<<"Case #"<<k<<":"<<endl;
               printf("The minimal distance is: %.2f\n",sum);  //输出sum
               cin>>n;
               if(n != 0) printf("\n");
     }
}

2. 最小生成树(Prim算法)

/**** **** **** **** **** ****

  • Function Name : 最小生成树(Prim算法)
  • Description : ZJU 1203 Swordfish O(N^2)
    **** **** **** **** **** ****/
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
double sum, arr_list[101][101], min;
int i, j, k=0, n;

struct struct_a
{
     float x;
     float y;
};
struct_a arr_xy[101];
struct struct_b
{
     int point;
     float lowcost;
};
struct_b closedge[101];

void prim(int n)   //prim  需要准备:n顶点数 arr_list[][]顶点的邻接矩阵也是从0开始计数
{
     int i,j,k;
     k=0;
     for(j=0; j<n ;j++) {
          if(j != k) {
               closedge[j].point = k;
               closedge[j].lowcost = arr_list[k][j];
          }
     }
     closedge[k].lowcost=0;
     for(i=0; i<n ;i++) {
          min=10000;
          for(j=0; j<n ;j++) {
               if (closedge[j].lowcost != 0 && closedge[j].lowcost < min) {
                    k = j;
                    min = closedge[j].lowcost;
               }
          }
          sum += closedge[k].lowcost;  //不要改成sum+=min;  sum即为所求值
          closedge[k].lowcost = 0;
          for(j=0; j<n ;j++) {
               if(arr_list[k][j] < closedge[j].lowcost) {
                    closedge[j].point = k;
                    closedge[j].lowcost = arr_list[k][j];
               }
          }
     }
}
/*
   arr_list[][]= Wij 如果Vi, Vj有边
                 0   如果i=j
                 无限大 如果没有边
*/
int main()
{
     cin>>n;
     while(n != 0) {
          sum=0;
          k++;
          for(i=0; i<n ;i++)
               cin>>arr_xy[i].x>>arr_xy[i].y;
          for(i=0; i<n ;i++)
               for(j=0; j<n ;j++) //得到邻接矩阵arr_list[][]
                    arr_list[i][j]=arr_list[j][i]=sqrt((arr_xy[i].x-arr_xy[j].x)*(arr_xy[i].x-arr_xy[j].x)+(arr_xy[i].y-arr_xy[j].y)*(arr_xy[i].y-arr_xy[j].y));
          prim(n);

          cout<<"Case #"<<k<<":"<<endl;
          printf("The minimal distance is: %.2f\n",sum);
          cin>>n;
          if(n!=0)     printf("\n");
     }
}

3. 单源最短路径(Bellman-ford算法)

/**** **** **** **** **** ****

  • Function Name : 单源最短路径(Bellman-ford算法)
  • Description : 可允许有负权
    **** **** **** **** **** ****/
#include <stdio.h>
#define MAX 100
#define MAXNUM 1000000

typedef struct graphnode
{
     int vexnum; //顶点数
     int arcnum; //边数
     int gra[MAX][MAX]; //图
}Graph;
Graph *G;
//arc数组中存储的第一个顶点到其他顶点的最短路径
//结果存在dis数组中
int dis[MAX];
int arc[MAX][MAX];

void bellman(Graph *G)
{
     int i,j;
     bool sign;
     for(i=0; i < G->vexnum ;i++)     dis[i]=MAXNUM;
     dis[1] = 0;
     sign = true;
     for(i=1; i < G->vexnum ;i++) {
          sign = false;
          for(j=0; j < G->arcnum ;j++) {
               if(dis[ arc[j][0] ] < MAXNUM 
                    && dis[ arc[j][1] ] > dis[ arc[j][0] ] + G->gra[ arc[j][0] ][ arc[j][1] ])
               {
                    dis[ arc[j][1] ]=dis[ arc[j][0] ] + G->gra[ arc[j][0] ][ arc[j][1] ];
                    sign = true;
               }
          }
     }
     return;
}

4. 单源最短路径(Dijkstra算法)

/**** **** **** **** **** ****

  • Function Name : 单源最短路径 (Dijkstra算法)
  • Description : 贪心, O(N^2), 不能有负权
    **** **** **** **** **** ****/
int matrix[200][200],n;          //matrix[][], 30000表示无限大,即无边.否则为有边,其值为边的权值
void Dijkstra(int x,int y)     //起点Vx 终点Vy
{
     int i,j,k,path[40000],mark[40000];
     int min,dist[40000];   
     for(i=1;i<=n;i++) {
          mark[i] = 0;
          dist[i] = matrix[x][i];
          path[i] = x;
     }
     mark[x] = 1;     
     do {
          min=30000;
          k=0;
          for(i=1;i<=n;i++)
               if(mark[i]==0 && dist[i]<min) {
                    min = dist[i];
                    k = i;
               }
          if(k) {
               mark[k] = 1;
               for(i=1;i<=n;i++)
                    if(matrix[k][i]<30000 && min+matrix[k][i]<dist[i]) {
                         dist[i] = min + matrix[k][i];
                         path[i] = k;
                    }
          }
     }while(k);
     cout<<dist[y]<<endl;     //dist[y] 的值就是从Vx 到 Vy 的最短路径值
     //如果希望得到路径,加入如下代码:
     do {
          cout<<k<<"<--";
          k = path[k];
     }while(k!=x);
     cout<<x<<endl;
}

5. 全源最短路径(Folyd算法)

/**** **** **** **** **** ****

  • Function Name : 全源最短路径(Folyd算法)
  • Description : DP, O(N^3)
    **** **** **** **** **** ****/
//初始化
//min_graph[i][j]=graph[i][j];
//path[i][j]=j;
void Floyd()
{
    int i,j,k;
    for(k=0;k<vertex_number;k++) {
        for(i=0;i<vertex_number;i++) {
            for(j=0;j<vertex_number;j++) {
                if((graph[i][k]==-1) || (graph[k][j]==-1))     continue;
                if((min_graph[i][j]==-1) || (min_graph[i][j] > graph[i][k]+graph[k][j]))
                {
                    min_graph[i][j] = graph[i][k]+graph[k][j];     /*最短路径值*/ 
                    path[i][j] = k;     /*最短路径*/ 
                }                
            }
        }
    }
}

6. 拓扑排序

/**** **** **** **** **** ****

  • Function Name : 拓扑排序
    **** **** **** **** **** ****/
//degree[]    每个结点的入度
//f[]        	每个结点所在的层
void Toplogical_sort()
{   
     int i,j;
     bool p=true;
     top=0;
     while(p) {
          p=false;
          top++;
          for(i=1;i<=n;i++)
            if(degree[i]==0) {
                    p=true;
                    f[i]=top;
               }  
          for(i=1;i<=n;i++)
               if(f[i]==top) {
                    for(j=1;j<=n;j++)
                         if(map[i][j])     degree[j]--;
                    degree[i]=-1;   
               }         
     }  
    top--;    
} 

7. 网络预流和最大流

int rel[1000][10000];     //全局变量
int pre[1000];
//计算网络流
//如果是二分图的匹配, 可以先对其进行网络预流以简化后续的查找
int pre_flow(int n,vector<int> * v)
{
     int ret = 0;
     int i,j,t,t1;
     
     for(i = 0 ; i < v[0].size() ; i++){
          t = v[0][i];   //t是与节点0相邻接的点
          for(j = 0 ; j < v[t].size() ; j++){
               t1 = v[t][j];     //与t相邻接的点
               if(rel[t1][n - 1] > 0){
                    ret++;
                    rel[0][t]--, rel[t][0]++;
                    rel[t][t1]--, rel[t1][t]++;
                    rel[t1][n - 1]--, rel[n - 1][t1]++;
                    break;
               }
          }
     }     
     return ret;
}
/* 
网络中求最大流
参数含义:    n代表网络中节点数,第0节点为源点, 第n-1节点为汇点
            rel是个二维数组, rel[i][j]代表从节点i到节点j的流量
            v[]是一个节点数组, v[i]包含与节点i相邻接的所有节点
返回值:        最大流量
*/
int max_flow(int n,vector<int> * v)
{
     int   ret = 0,i;
     int   t,t1,tm;
     queue<int> q;
     
     const int Infinite = 2000000000;
     
     while(1){
          for(t = 0 ; t < n ; t++)     pre[t] = -1;
          while(!q.empty()) q.pop();
          q.push(0);
          while(!q.empty()){  //find a augmenting path using breath-first search
               t = q.front();
               q.pop();
               if(t == n - 1) break;  //到达汇点
               for(i = 0 ; i < v[t].size() ; i++){  //对于t相邻接的所有点查找可行路径
                    t1 = v[t][i];
                    if(rel[t][t1] > 0 && pre[t1] == -1){
                         pre[t1] = t;
                         q.push(t1);
                    }
               }
          }
          if(q.empty() && t != n - 1) break;
          tm = Infinite;      //此处寻找路径最小值在二分图中可省略
          while(t != 0){      //find the minimal num in the path
               t1 = pre[t];
               if(rel[t1][t] < tm)     tm = rel[t1][t];
               t = t1;
          }
          //  tm = 1;  //二分图中
          t = n - 1;
          while(t != 0){           //change the relation
               t1 = pre[t];
               rel[t1][t] -= tm;
               rel[t][t1] += tm;
               t = t1;
          }
          ret += tm;
     }
     return ret;
}

8. 网络最小费用最大流

/**** **** **** **** **** ****
网络中最小费用最大流
参数含义: np代表网络中的总节点数, v是网络节点的邻接表
cost为最后求得的最小费用, mf为求得的最大流
算法: 初始最小费用及最大流均为0,不断寻找可增广路
增广路对应的单位费用最小并且可流
修改残留网络及cost,mf. 直到无可增广路为止。
**** **** **** **** **** ****/

const int Max = 200000000;

vector<int> v[110];     //存储每个节点的邻接点
int flow[110][110];     //flow[i][j]代表由i节点到j节点的可行流量
int fcost[110][110];    //fcost[i][j]代表由i节点到j节点的单位流量费用
int ct[110];    //ct[i]代表单位容量到达i节点的最小费用
int pre[110];   //可行节点的前驱节点
void min_cost_max_flow(int np,const vector<int> * v,int & cost,int & mf)
{
     int t,t1,tm,i,j;
     bool out;
     
     cost = 0,mf = 0;
     while(1){
          for(i = 0 ; i < np ; i++)     pre[i] = -1,ct[i] = Max;
          ct[0] = 0;
          while(1){
               out = false;
               for(i = 0 ; i < np ; i++){
                    for(j = 0 ; j < v[i].size() ; j++){
                         t = v[i][j];
                         if(flow[i][t] > 0 && ct[i] != Max && ct[i] + fcost[i][t] < ct[t]){
                              out = true;
                              ct[t] = ct[i] + fcost[i][t];
                              pre[t] = i;
                         }
                    }
               }
               if(!out) break;
          }
          if(ct[np - 1] != Max){
               t = np - 1;
               tm = Max;      //此处寻找流量最小值
               while(t != 0){      //find the minimal num in the path
                    t1 = pre[t];
                    if(flow[t1][t] < tm)     tm = flow[t1][t];
                    t = t1;
               }
               mf += tm;              //流量增加
               t = np - 1;
               while(t != 0){           //change the relation
                    t1 = pre[t];
                    flow[t1][t] -= tm;
                    flow[t][t1] += tm;
                    cost += tm * fcost[t1][t];   //费用增加
                    t = t1;
               }
          }
          else break;
     }
}



9. 网络最大流(高度标号预流推进)

/*
函数接口: int Relabel_To_Front(int s,int d)
参数含义: s为源点,d为汇点
返回值 : 网络最大流
调用函数前的初始化工作:ver置为网络中节点的个数,c[i][j]代表节点i到
节点j的流量,vl[i]存放i与相邻的所有节点
其它全局变量均初始化为零
*/

const int VEX = 405;  //网络中顶点数
const int HMAX = 810; //最大高度的定义,只要大于顶点的2倍就可以了

int f[VEX][VEX];  //流量
int c[VEX][VEX];  //边最大容量
int h[VEX];       //节点高度
int e[VEX];       //节点容量
int ver;          //节点数目
vector<int> vl[VEX];  //邻接表,vl[i]存放与i相邻的节点

void Push(int u,int v)  //流推进,由节点u推向v
{
     int cf = c[u][v] - f[u][v];   //u,v边的容量
     int d = e[u] < cf ? e[u] : cf;
     
     f[u][v] += d;
     f[v][u] = -f[u][v];
     e[u] -= d;
     e[v] += d;
}

void Relabel(int u)  //对u重新标号
{
     int i,t,cf;
     int hmin = HMAX;
     
     for(i = 0 ; i < vl[u].size() ; i++){  //寻找相邻最低点
          t = vl[u][i];
          cf = c[u][t] - f[u][t];
          if(cf > 0 && h[u] <= h[t] && h[t] < hmin)
               hmin = h[t];
     }
     h[u] = hmin + 1;
}

void Init_Preflow(int s)  //初始化网络流,s为源点
{
     int i;
     int u;
     
     h[s] = ver;  //初始化高度
     for(i = 0 ; i < vl[s].size() ; i++){
          u = vl[s][i];
          f[s][u] = c[s][u];
          f[u][s] = -c[s][u];
          e[u] = c[s][u];
          e[s] -= c[s][u];
     }
}

void Discharge(int u)
{
     int i = 0;
     int cf,v;
     
     if(vl[u].size() == 0) return;
     while(e[u] > 0){
          if(i < vl[u].size()) {
               v = vl[u][i];
               cf = c[u][v] - f[u][v];
          }
          if(i >= vl[u].size()){
               Relabel(u);
               i = 0;
          }
          else if(cf > 0 && h[u] == h[v] + 1)
               Push(u,v);
          else
               i++;
     }
}

int Relabel_To_Front(int s,int d) //s为源点,d为汇点
{
     int u,i,old_h;
     list<int> l;
     list<int>::iterator iter;
     
     Init_Preflow(s);
     
     iter = l.begin();
     for(i = 0 ; i < ver ; i++){
          if(i != s && i != d)
               l.insert(iter,i);
     }
     iter = l.begin();
     while(iter != l.end()){
          u = *iter;
          old_h = h[u];
          Discharge(u);
          if(h[u] > old_h){
               l.erase(iter);
               l.insert(l.begin(),u);
               iter = l.begin();
          }
          iter++;
     }
     return e[ver - 1];
}

10. 最大团

/**** **** **** **** **** ****

  • Function Name : 最大团
  • Description : ZJU 1492 Maximum Clique
  • 团: 指G的一个完全子图, 该子图不包含在任何其他的完全子图当中
  • 最大团: 指其中包含顶点最多的团
    **** **** **** **** **** ****/
#include<stdio.h>
#include<string.h>

int joint[50][50];
int Size, MAX, DP[50];
bool find;
//返回-1表示邻接顶点集已经为空集,否则返回第一个公共顶点
int reachEnd(int start, int sets[])
{   
     int lp;
     for(lp=start; lp<Size ;lp++)
          if(sets[lp])     return lp;
     return -1;          
}

void DFS(int Visit[], int start, int depth)
{
    int loop, first, sets[50], SET[50];
     memcpy(sets,Visit,Size*4);
     memcpy(SET,Visit,Size*4);
     if(( first=reachEnd(start,sets) ) == -1) {
          if(depth > MAX) {
               MAX = depth;
               find = true;     
        }
          return ;
     }
     while(first != -1) {
          if(depth + Size - start <= MAX)//不可能找到最优解
               return ;
          if(depth + DP[first] <= MAX)//不可能找到最优解
               return;
          sets[first] = 0;
          SET[first] = 0;//从邻接顶点集中清除first顶点
          for(loop=first+1; loop < Size ;loop++)  //合并邻接顶点集
               if(SET[loop]==1 && joint[first][loop]==1)     sets[loop]=1;
               else     sets[loop]=0;
          DFS(sets,first,depth+1);
          if(find)     return ;
          first = reachEnd(first,SET);//更新接点    
     } 
}

int main()
{
     int loop, lp, Visit[50];     
     while(scanf("%d",&Size)!=EOF && Size!=0) {
          for(loop=0; loop < Size ;loop++)
               for(lp=0; lp < Size ;lp++)
                    scanf("%d",joint[loop]+lp);
          MAX=0;
          for(loop=Size-1; loop >= 0 ;loop--) {
               find=false;
               memcpy(Visit, joint[loop], Size*4); 
               DFS(Visit, loop, 1);
               DP[loop] = MAX;     
          }
          printf("%d\n",DP[0]);     
     }
}

11. 最大二分图匹配(匈牙利算法)

/**** **** **** **** **** ****

  • Function Name : 最大二分图匹配(匈牙利算法)
  • Description : HDOJ 2063 过山车
  • 二分图: 指所有顶点分成集合M和N, M或N中任意两个在同一集合中的点互不相连
  • 匹配: 一组边顶点分别在两个集合中, 并且任意两条边都没有相同顶点
  • 最大匹配: 所能得到的最大的边的个数
    **** **** **** **** **** ****/
#include<cstdio>
#include<memory>
#include<vector>
using namespace std;
const int Max=1100;
vector< vector<int> > Bmap;
int n, m, k, nm;
int mark[Max];
bool flag[Max];

bool dfs(int pos)
{
    int i, pre, tp;
    for(i=0; i < Bmap[pos].size() ;i++) {
        tp = Bmap[pos][i];
        if( !flag[tp] ) {
            flag[tp] = true;
            pre = mark[tp];
            mark[tp] = pos;
            if(pre==-1 || dfs(pre))     return true;
            mark[tp] = pre;
        }
    }
    return false;
}

inline int Max_Match()
{
    int mmax = 0, i;
    for(i=1; i <= m ;i++) {
        memset(flag,0,sizeof(flag));
        if( dfs(i) )     mmax++;
    }
    return mmax;
}

int main()
{
    int i, j, id, id2;
    while(scanf("%d", &k)==1 && k) {
          scanf("%d%d",&m, &n);
          nm = n + m;
          Bmap.clear();     Bmap.resize(nm+10);
          memset(mark,-1,sizeof(mark));
          for(j=0; j < k ;j++) {
               scanf("%d %d", &id, &id2);
               id2 += m;
               Bmap[id].push_back(id2);
          }
        printf("%d\n", Max_Match());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值