程序竞赛中的图论

/*==================================================*\ 
 | DAG的深度优先搜索标记 
 | INIT: edge[][]邻接矩阵; pre[], post[], tag全置0; 
 | CALL: dfstag(i, n);   pre/post:开始/结束时间 
\*==================================================*/ 
int edge[V][V], pre[V], post[V], tag; 
void dfstag(int cur, int n) 
{  // vertex: 0 ~ n-1  
    pre[cur] = ++tag;  
    for (int i=0; i<n; ++i) if (edge[cur][i]) {   
        if (0 == pre[i]) {    
            printf("Tree Edge!\n");    
            dfstag(i, n);   
        } else {    
            if (0 == post[i])  printf("Back Edge!\n");    
            else if (pre[i] > pre[cur])  printf("Down Edge!\n");    
            else  printf("Cross Edge!\n");   
        }  
    }  
    post[cur] = ++tag; 
}
/*==================================================*\ 
 | 无向图找桥 
 | INIT: edge[][]邻接矩阵;vis[],pre[],anc[],bridge 置0; 
 | CALL: dfs(0, -1, 1, n); 
\*==================================================*/ 
int bridge, edge[V][V], anc[V], pre[V], vis[V]; 
void dfs(int cur, int father, int dep, int n) 
{ // vertex: 0 ~ n-1  
    if (bridge) return;  
    vis[cur] = 1; pre[cur] = anc[cur] = dep;  
    for (int i=0; i<n; ++i) if (edge[cur][i]) {   
        if (i != father && 1 == vis[i]) {    
            if (pre[i] < anc[cur]) anc[cur] = pre[i];//back edge   
        }   
        if (0 == vis[i]) {          //tree edge    
            dfs(i, cur, dep+1, n);    
            if (bridge) return;    
            if (anc[i] < anc[cur]) anc[cur] = anc[i];    
            if (anc[i] > pre[cur]) { bridge = 1; return; }   
        }  
    }  
    vis[cur] = 2; 
} 
/*==================================================*\ 
 | 无向图连通度(割) 
 | INIT: edge[][]邻接矩阵;vis[],pre[],anc[],deg[]置为0; 
 | CALL: dfs(0, -1, 1, n); 
 | k=deg[0], deg[i]+1(i=1…n-1)为删除该节点后得到的连通图个数 
 | 注意:0作为根比较特殊! 
\*==================================================*/ 
int edge[V][V], anc[V], pre[V], vis[V], deg[V]; 
void dfs(int cur, int father, int dep, int n) 
{// vertex: 0 ~ n-1  
    int cnt = 0;  
    vis[cur] = 1; pre[cur] = anc[cur] = dep;  
    for (int i=0; i<n; ++i) if (edge[cur][i]) {   
        if (i != father && 1 == vis[i]) {    
            if (pre[i] < anc[cur]) 
                anc[cur] = pre[i]; //back edge   
        }   
        if (0 == vis[i]) {          //tree edge    
            dfs(i, cur, dep+1, n);    
            ++cnt; // 分支个数    
            if (anc[i] < anc[cur]) anc[cur] = anc[i];    
            if ((cur==0 && cnt>1) || (cnt!=0 && anc[i]>=pre[cur]))     
                ++deg[cur];  // link degree of a vertex   
        }  
    }  
    vis[cur] = 2; 
} 
/*==================================================*\ 
 | 最大团问题 DP + DFS 
 | INIT: g[][]邻接矩阵; 
 | CALL: res = clique(n); 
\*==================================================*/ 
int g[V][V], dp[V], stk[V][V], mx; 
int dfs(int n, int ns, int dep){  
    if (0 == ns) {   
        if (dep > mx) mx = dep;   
        return 1;  
    }  
    int i, j, k, p, cnt;  
    for (i = 0; i < ns; i++) {   
        k = stk[dep][i]; cnt = 0;   
        if (dep + n - k <= mx) return 0;   
        if (dep + dp[k] <= mx) return 0;   
        for (j = i + 1; j < ns; j++) {    
            p = stk[dep][j];    
            if (g[k][p]) stk[dep + 1][cnt++] = p;   
        }   
        dfs(n, cnt, dep + 1);  
    }  
    return 1; 
} 
int clique(int n){  
    int i, j, ns;  
    for (mx = 0,  i = n - 1; i >= 0; i--) { 
        // vertex: 0 ~ n-1   
        for (ns = 0, j = i + 1; j < n; j++)    
            if (g[i][j]) stk[1][ ns++ ] = j;   
        dfs(n, ns, 1); dp[i] = mx;  
    }  
    return mx; 
}
/*==================================================*\ 
 | 欧拉路径O(E) 
 | INIT: adj[][]置为图的邻接表; cnt[a]为a点的邻接点个数; 
 | CALL: elpath(0);   注意:不要有自向边 
\*==================================================*/ 
int adj[V][V], idx[V][V], cnt[V], stk[V], top; 
int path(int v) {  
    for (int w ; cnt[v] > 0; v = w) {   
        stk[ top++ ] = v;   
        w = adj[v][ --cnt[v] ];   
        adj[w][ idx[w][v] ] = adj[w][ --cnt[w] ];  
        // 处理的是无向图—-边是双向的,删除v->w后,还要处理删除w->v  
    } 
    return v; 
} 
void elpath (int b, int n){          // begin from b  
    int i, j;  
    for (i = 0; i < n; ++i)         // vertex: 0 ~ n-1   
        for (j = 0; j < cnt[i]; ++j)    
            idx[i][ adj[i][j] ] = j;  
    printf("%d", b);  
    for (top = 0; path(b) == b && top != 0; ) {   
        b = stk[ --top ];   
        printf("-%d", b);  
    }  
    printf("\n"); 
} 
/*==================================================*\ 
 | Dijkstra数组实现 O(N^2)
 | Dijkstra --- 数组实现(在此基础上可直接改为STL的Queue实现) 
 | lowcost[] --- beg到其他点的近距离
 | path[] -- beg为根展开的树,记录父结点 
\*==================================================*/ 
#define INF 0x03F3F3F3F 
const int N; 
int path[N], vis[N]; 
void Dijkstra(int cost[][N], int lowcost[N], int n, int beg) {  
    int i, j, min;  
    memset(vis, 0, sizeof(vis));  
    vis[beg] = 1;  
    for (i=0; i<n; i++) {   
        lowcost[i] = cost[beg][i]; 
        path[i] = beg;  
    }  
    lowcost[beg] = 0;  
    path[beg] = -1; // 树根的标记  
    int pre = beg;  
    for (i=1; i<n; i++) {         
        min = INF;  
        for (j=0; j<n; j++)          
        // 下面的加法可能导致溢出,INF不能取太大    
            if (vis[j]==0 && lowcost[pre]+cost[pre][j]<lowcost[j]) {     
                lowcost[j] = lowcost[pre] + cost[pre][j];     
                path[j] = pre;    
        }   
        for (j=0; j<n; j++)    
            if (vis[j] == 0 && lowcost[j] < min){     
                min = lowcost[j]; 
                pre = j;    
        }         
        vis[pre] = 1;  
    } 
} 
/*==================================================*\ 
 | Dijkstra O(E * log E) 
 | INIT: 调用init(nv, ne)读入边并初始化; 
 | CALL: dijkstra(n, src); dist[i]为src到i的短距离
 \*==================================================*/ 
#define typec int              // type of cost const 
typec inf = 0x3f3f3f3f;        // max of cost 
typec cost[E], dist[V]; 
int e, pnt[E], nxt[E], head[V], prev[V], vis[V]; 
struct qnode {  
    int v; typec c;  
    qnode (int vv = 0, typec cc = 0) : v(vv), c(cc) {}  
    bool operator < (const qnode& r) const { return c>r.c; } 
}; 
void dijkstra(int n, const int src) {  
    qnode mv;  
    int i, j, k, pre;  
    priority_queue<qnode> que;  
    vis[src] = 1; dist[src] = 0;  
    que.push(qnode(src, 0));  
    for (pre = src, i=1; i<n; i++) {   
        for (j = head[pre]; j != -1; j = nxt[j]) {    
            k = pnt[j];    
            if (vis[k] == 0 && dist[pre] + cost[j] < dist[k]){     
                dist[k] = dist[pre] + cost[j];     
                que.push(qnode(pnt[j], dist[k]));     
                prev[k] = pre;    
            }   
        }  
        while (!que.empty() && vis[que.top().v] == 1) que.pop();   
        if (que.empty()) break;   
        mv = que.top(); que.pop();   
        vis[pre = mv.v] = 1;  
    } 
} 
inline void addedge(int u, int v, typec c) {  
    pnt[e] = v; cost[e] = c; 
    nxt[e] = head[u]; head[u] = e++; 
} 
void init(int nv, int ne) {  
    int i, u, v; 
    typec c;  
    e = 0;  
    memset(head, -1, sizeof(head));  
    memset(vis, 0, sizeof(vis));  
    memset(prev, -1, sizeof(prev));  
    for (i = 0; i < nv; i++) dist[i] = inf;  
    for (i = 0; i < ne; ++i) {   
        scanf("%d%d%d", &u, &v, &c); // %d: type of cost   
        addedge(u, v, c);      // vertex: 0 ~ n-1, 单向边  
    } 
}
/*==================================================*\ 
 | BellmanFord单源最短路O(VE) 
 | 能在一般情况下,包括存在负权边的情况下,解决单源短路径问题 
 | INIT: edge[E][3]为边表 
 | CALL: bellman(src);有负环返回0;dist[i]为src到i的短距 
 | 可以解决差分约束系统: 需要首先构造约束图,构造不等式时>=表示求 小值, 
 | 作为长路,<=表示求最大值, 作为最短路  (v-u <= c:a[u][v] = c) 
 \*==================================================*/
#define typec int            // type of cost 
const typec inf=0x3f3f3f3f;  // max of cost 
int n, m, pre[V], edge[E][3]; 
typec dist[V]; 
int relax (int u, int v, typec c) {  
    if (dist[v] > dist[u] + c) {
        dist[v] = dist[u] + c;   
        pre[v] = u; 
        return 1;  
    }  
    return 0; 
} 
int bellman (int src) {  
    int i, j;  
    for (i=0; i<n; ++i) {   
        dist[i] = inf; pre[i] = -1;  
    }  
    dist[src] = 0; bool flag;  
    for (i=1; i<n; ++i) { 
        flag = false; // 优化 
        for (j=0; j<m; ++j) {   
            if( 1 == relax(edge[j][0], edge[j][1], edge[j][2]) ) 
                flag = true;     
        }          
        if( !flag ) break; 
    }  
    for (j=0; j<m; ++j) {   
        if (1 == relax(edge[j][0], edge[j][1], edge[j][2]))    
            return 0;  // 有负圈  
    }  
    return 1; 
} 
/*==================================================*\ 
 | 第K短路(Dijkstra) 
 | dij变形,可以证明每个点经过的次数为小于等于K,所有把dij的数组dist
 | 由一维变成2维,记录经过该点1次,2次。。。k次的小值。 
 | 输出dist[n-1][k]即可 
\*==================================================*/ 
//WHU1603  int g[1010][1010]; 
int n,m,x; 
const int INF=1000000000; 
int v[1010]; int dist[1010][20]; 
int main() {     
    while (scanf("%d%d%d",&n,&m,&x)!=EOF) {         
        for (int i=1; i<=n; i++)    
            for (int j=1;j<=n;j++) 
              g[i][j]=INF;   
        for (int i=0; i<m; i++) {  
            int p,q,r;    
            scanf("%d%d%d",&p,&q,&r);  
            if (r<g[p][q]) g[p][q]=r;        
        }  
        for (int i=1;i<=n;i++) {     
            v[i]=0;             
            for (int j=0;j<=x;j++)  
                dist[i][j]=INF;         
        }             
        dist[1][0]=0;         
        dist[0][0]=INF;         
        while (1) {             
            int k=0;             
            for (int i=1;i<=n;i++)   
                if (v[i]<x && dist[i][v[i]]<dist[k][0])    
                    k=i;             
                if (k==0) break;             
                if (k==n && v[n]==x-1) break;             
            for (int i=1;i<=n;i++) {                 
                if (v[i]<x && dist[k][v[k]]+g[k][i]<dist[i][x]) {
                    dist[i][x]=dist[k][v[k]]+g[k][i];                     
                for (int j=x;j>0;j--)                         
                    if (dist[i][j]<dist[i][j-1])                             
                        swap(dist[i][j],dist[i][j-1]);                 
                }              
            }                     
            v[k]++;         
        }                 
        if (dist[n][x-1]<INF) printf("%d\n",dist[n][x-1]);         
        else printf("-1\n");     
    }     
    return 0;     
}
/*==================================================*\ 
 | Prim求MST 
 | INIT: cost[][]耗费矩阵(inf为无穷大); 
 | CALL: prim(cost, n); 返回-1代表原图不连通;
\*==================================================*/ 
#define typec int              // type of cost 
const typec inf = 0x3f3f3f3f;  // max of cost 
int vis[V]; typec lowc[V]; 
typec prim(typec cost[][V], int n)    // vertex: 0 ~ n-1 
{  
    int i, j, p;  
    typec minc, res = 0;  
    memset(vis, 0, sizeof(vis));  
    vis[0] = 1;  
    for (i=1; i<n; i++) lowc[i] = cost[0][i];  
    for (i=1; i<n; i++) {         
        minc = inf; p = -1;         
        for (j=0; j<n; j++) 
            if (0 == vis[j] && minc > lowc[j]) {    
                minc = lowc[j]; p = j;  
            }         
        if (inf == minc) return -1;        // 原图不连通         
        res += minc; vis[p] = 1;         
        for (j=0; j<n; j++)    
            if (0 == vis[j] && lowc[j] > cost[p][j])     
            lowc[j] = cost[p][j]; 
    }  
    return res; 
}
/*==================================================*\ 
 | 有向图最小树形图 
 | INIT: eg置为边表; res置为0; cp[i]置为i; 
 | CALL: dirtree(root, nv, ne); res是结果; 
\*==================================================*/ 
#define typec int                // type of res 
const typec inf = 0x3f3f3f3f;    // max of res 
typec res, dis[V]; 
int to[V], cp[V], tag[V]; 
struct Edge { int u, v; typec c; } eg[E]; 
int iroot(int i){  
    if (cp[i] == i) return i;  
    return cp[i] = iroot(cp[i]); 
} 
int dirtree(int root, int nv, int ne) // root: 树根 
{ // vertex: 0 ~ n-1  
    int i, j, k, circle = 0;  
    memset(tag, -1, sizeof(tag));  
    memset(to, -1, sizeof(to));  
    for (i = 0; i < nv; ++i) dis[i] = inf;  
    for (j = 0; j < ne; ++j) {   
        i = iroot(eg[j].u); 
        k = iroot(eg[j].v);   
        if (k != i && dis[k] > eg[j].c) {    
            dis[k] = eg[j].c;    
            to[k] = i;   
        }  
    }  
    to[root] = -1;  dis[root] = 0;  tag[root] = root;  
    for (i = 0; i < nv; ++i) if (cp[i] == i && -1 == tag[i]) {   
        j = i;   
        for ( ; j != -1 && tag[j] == -1; j = to[j]) 
            tag[j] = i;   
        if (j == -1) return 0;   
        if (tag[j] == i) {    
            circle = 1; tag[j] = -2;    
            for (k = to[j]; k != j; k = to[k]) 
                tag[k] = -2;   
        }  
    }  
    if (circle) {   
        for (j = 0; j < ne; ++j) {    
            i = iroot(eg[j].u); 
            k = iroot(eg[j].v);    
            if (k != i && tag[k] == -2) 
                eg[j].c -= dis[k];   
        }   
        for (i = 0; i < nv; ++i) if (tag[i] == -2) {    
            res += dis[i]; tag[i] = 0;    
            for (j = to[i]; j != i; j = to[j]) {     
                res += dis[j]; cp[j] = i; tag[j] = 0;    
        }   
    }   
    if (0 == dirtree(root, nv, ne)) return 0;  
    } else {   
        for (i = 0; i < nv; ++i) 
            if (cp[i] == i) res += dis[i];  
    }  
    return 1;          // 若返回0代表原图不连通 
} 
/*==================================================*\ 
 | Tarjan强连通分量  
 | INIT: vec[]为邻接表; stop, cnt, scnt置0; pre[]置-1;  
 | CALL: for(i=0; i<n; ++i) if(-1==pre[i]) tarjan(i, n); 
\*==================================================*/ 
vector<int> vec[V]; 
int id[V], pre[V], low[V], s[V], stop, cnt, scnt; 
void tarjan(int v, int n)       // vertex: 0 ~ n-1 
{  
    int t, minc = low[v] = pre[v] = cnt++;  
    vector<int>::iterator pv;  
    s[stop++] = v;  
    for (pv = vec[v].begin(); pv != vec[v].end(); ++pv) {   
        if(-1 == pre[*pv]) tarjan(*pv, n);   
        if(low[*pv] < minc) minc=low[*pv];  
    }  
    if(minc < low[v]) {   
        low[v] = minc; return;  
    }     
    do {   
        id[t = s[--stop]] = scnt; 
        low[t] = n;     
    } while(t != v);  
    ++scnt;            // 强连通分量的个数 
} 
/*==================================================*\ 
 | 拓扑排序 
 | INIT:edge[][]置为图的邻接矩阵;count[0…i…n-1]:顶点i的入度. 
\*==================================================*/ 
void TopoOrder(int n){  
    int  i, top = -1;  
    for( i=0; i < n; ++i ) {
        if( count[i] == 0 ) { // 下标模拟堆栈 
            count[i] = top; top = i;   
        }  
        for( i=0; i < n; ++i ) {
            if( top == -1 ) {
                printf("存在回路\n"); 
                return ; 
            } else {    
                int j = top; top = count[top];    
                printf("%d", j);    
                for( int k=0; k < n; ++k )     
                    if( edge[j][k] && (--count[k]) == 0  ){      
                        count[k] = top; top = k;     
                    }   
            } 
        }
    }
} 
/*==================================================*\ 
 | 有向图最小点基(邻接阵)O(n^2)  
 | 点基B满足:对于任意一个顶点Vj,一定存在B中的一个Vi,使得Vi是Vj 
 | 的前代。 
\*==================================================*/ 
//返回点基大小和点基 
//传入图的大小n和邻接阵mat,不相邻点边权0 
//需要调用强连通分支 
#define MAXN 100 
int base_vertex(int n,int mat[][MAXN],int* sets) {  
    int ret=0,id[MAXN],v[MAXN],i,j;  
    j=find_components(n,mat,id);  
    for (i=0;i<j;v[i++]=1);  
    for (i=0;i<n;i++)   
        for (j=0;j<n;j++)    
            if (id[i]!=id[j]&&mat[i][j])     
                v[id[j]-1]=0;  
    for (i=0;i<n;i++)   
        if (v[id[i]-1])    
            v[id[sets[ret++]=i]-1]=0;  
    return ret; 
} 

猜你喜欢

转载自blog.csdn.net/qq_42024195/article/details/88926895