图论模板简单整理

唔,图论部分暂时就看到这里了,整理一下最近学的东西

//最短路
//dijkstra
void dijkstra() {
    memset(vis,0,sizeof(vis));
    for(int i = 1;i <= n;i++) {
        d[i] = -1;
    }
    d[n] = 1;
    for(int k = 1;k <= n;k++) {
        double maxv = -1; int x = n;
        for(int i = 1;i <= n;i++) if(!vis[i] && d[i] > maxv) {
            maxv = d[x = i];
        }
        vis[x] = true;
        for(int i = 1;i <= n;i++) if(!vis[i] && p[x][i] >= 0) {
            if(d[i] == -1) d[i] = d[x] * p[x][i];
            else d[i] = max(d[i],d[x] * p[x][i]);
        }
    }
}

//dijkstra + heap

void dijkstra(int *v) {
    memset(vis,0,sizeof(vis));
    for(int i = 1;i <= n;i++) d[i] = INF;
    d[1] = 0;
    priority_queue<Node> q;
    q.push(Node(d[1],1));
    while(!q.empty()) {
        Node now = q.top(); q.pop();
        int x = now.b;
        if(vis[x]) continue;
        vis[x] = true;
        for(int i = first[x];i != 0;i = nxt[i]) {
            if(d[v[i]] > d[x] + w[i]) {
                d[v[i]] = d[x] + w[i];
                q.push(Node(d[v[i]],v[i]));
            }
        }
    }
}

//bellman-ford
void bellman_ford() {
    for(int i = 0;i < M;i++) d[i] = INF;
    d[0] = 0;
    for(int i = 0;i < M;i++) {
        for(int j = 0;j < M;j++) {
            for(int k = 0;k < M;k++) if(dist[j][k] < INF) {
                if(d[j] < INF) {
                    d[k] = min(d[k],d[j] + dist[j][k]);
                }
            }
        }
    }
    printf("%.2f\n",d[M - 1]);
}

//SPFA
void spfa(int *v,int *d) {
    memset(vis,0,sizeof(vis));
    for(int i = 1;i <= N;i++) d[i] = INF;
    d[X] = 0;
    queue<int> q;
    q.push(X);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = false;
        for(int i = first[u];i != 0;i = nxt[i]) {
            if(d[v[i]] > d[u] + w[i]) {
                d[v[i]] = d[u] + w[i];
                if(!vis[v[i]]) {
                    vis[v[i]] = true;
                    q.push(v[i]);
                }
            }
        }
    }
}


//欧拉路和欧拉回路
//寻找欧拉路并且输出路径
void dfs(int now) {
    for(int i = 0;i < e[now].size();i++) {
        if(e[now][i].vis == 0) {
            e[now][i].vis = 1;
            dfs(e[now][i].v);
            ans.push(e[now][i].str);
        }
    }
}

//网络流
//标号法
//Ford-Fulkerson
void solve() {
    memset(flow,0,sizeof(flow));
    alpha[s] = INF;
    while(1) {
        //初始化标号
        for(int i = 1;i <= t;i++) pre[i] = -2;
        pre[s] = -1;
        //初始化队列,源点入列
        qs = 0; qe = 1;
        q[qs] = s;
        //标号过程
        while(qs < qe && pre[t] == -2) {    //终点没有被标号并且队列非空
            int v = q[qs]; qs++;
            //printf("now v is %d\n",v);
            for(int i = 1;i <= t;i++) {
                //如果目标点没有被标号并且还有残余流量
                if(pre[i] == -2 && dist[v][i] - flow[v][i] != 0) {
                    pre[i] = v;
                    alpha[i] = min(alpha[v],dist[v][i] - flow[v][i]);
                    q[qe++] = i;
                }
            }
        }
        //没有找到到汇点的增广路,退出
        if(pre[t] == -2) {
            break;
        }
        //逆向更新
        int aval = alpha[t];
        for(int i = t;pre[i] != -1;i = pre[i]) {
            flow[pre[i]][i] += aval;
            flow[i][pre[i]] = -flow[pre[i]][i];
        }
    }
    //统计流量
    int ans = 0;
    for(int i = 1;i <= n;i++) {
        ans += flow[i][t];
    }
    printf("%d\n",ans);
}

//dinic

bool bfs() {
    qs = qe = 0;
    q[qe++] = s;
    memset(level,0,sizeof(level));
    level[s] = 1;
    while(qs < qe) {
        int v = q[qs++];
        if(v == t) break;
        for(int i = s;i <= t;i++) if(cap[v][i] && !level[i]) {
            level[i] = level[v] + 1;
            q[qe++] = i;
        }
    }
    return level[t];
}
 
int dfs(int now,int alpha) {
    int sum = 0;
    if(now == t) return alpha;
    for(int i = s;i <= t;i++) {
        if(level[i] == level[now] + 1 && alpha && cap[now][i]) {
            int ret = dfs(i,min(alpha,cap[now][i]));
            cap[now][i] -= ret;
            cap[i][now] += ret;
            alpha -= ret;
            sum += ret;
        }
    }
    return sum;
}
 
void dinic() {
    int ans = 0;
    while(bfs()) ans += dfs(s,INT_MAX);
    printf("%d\n",ans);
}

//混合图欧拉回路的判断
//先把所有的边看成是有向边,判断所有的点入度和出度之差是否是偶数,如果有奇数出现那么就不是欧拉回路。
//然后去掉图中所有的有向边,建立虚拟的源点和汇点,如果剩下的点中有入度大于出度的,就建立到汇点的边,容量为入度出度之差/2,如果有出度大于入读的,就建立到源点的边,容量同样为入度和出度之差的一半,做一遍最大流,流量和要调换的边的数量相等。
//满流的边需要调换


using namespace std;
 
typedef long long LL;
const int maxn = 205;
const int INF = INT_MAX / 3;
 
struct Edge {
    int u,v,cap;
    Edge(int u,int v,int cap):u(u),v(v),cap(cap) {}
};
 
int n,m,incnt[maxn],outcnt[maxn];
int deg[maxn],s,t;
vector<Edge> edges;
vector<int> e[maxn];
 
void adde(int u,int v,int w) {
    int m = edges.size();
    edges.push_back(Edge(u,v,w));
    edges.push_back(Edge(v,u,0));
    e[u].push_back(m);
    e[v].push_back(m ^ 1);
}
 
int level[maxn],q[maxn * 2],qs,qe;
bool bfs() {
    //建立层次网络
    memset(level,0,sizeof(level));
    level[s] = 1;
    qs = qe = 0;
    q[qe++] = s;
    while(qs < qe) {
        int now = q[qs++],nm = e[now].size();
        if(now == t) break;
        for(int i = 0;i < nm;i++) {
            Edge &ne = edges[e[now][i]];
            if(ne.cap && level[ne.v] == 0) {
                level[ne.v] = level[now] + 1;
                q[qe++] = ne.v;
            }
        }
    }
    return level[t];
}
 
int dfs(int now,int alpha) {
    if(now == t) return alpha;
    int sum = 0,nm = e[now].size();
    for(int i = 0;i < nm;i++) {
        Edge &ne = edges[e[now][i]];
        if(level[now] + 1 == level[ne.v] && ne.cap && alpha) {
            int ret = dfs(ne.v,min(alpha,ne.cap));
            ne.cap -= ret; edges[e[now][i] ^ 1].cap += ret;
            sum += ret; alpha -= ret;
        }
    }
    if(sum == 0) level[now] = -1;
    return sum;
}
 
void dinic() {
    while(bfs()) dfs(s,INF);
}
 
bool solve() {
    s = 0; t = n + 1;
    //判断入度出度之差是否为偶数
    for(int i = 1;i <= n;i++) {
        deg[i] = incnt[i] - outcnt[i];
        if(deg[i] & 1) return false;
    }
    //建立容量网络
    for(int i = 1;i <= n;i++) {
        //如果入度小于出度,建立从起点到这个点的边,容量为deg/2
        if(deg[i] < 0) adde(s,i,-deg[i] / 2);
        //如果出度大于入读,建立从当前点到汇点的边,容量同样为deg/2
        if(deg[i] > 0) adde(i,t,deg[i] / 2);
    }
    //计算最大流
    dinic();
    //判断从源点出发的所有边是否满流
    int m = e[s].size();
    for(int i = 0;i < m;i++) {
        if(edges[e[s][i]].cap != 0) return false;
    }
    return true;
}
 
int main() {
    int T; scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        edges.clear();
        for(int i = 0;i <= n + 1;i++) e[i].clear();
        memset(incnt,0,sizeof(incnt));
        memset(outcnt,0,sizeof(outcnt));
        for(int i = 1;i <= m;i++) {
            int u,v,c; scanf("%d%d%d",&u,&v,&c);
            //先将无向边全部作为有向边处理
            incnt[v]++; outcnt[u]++;
            //无向边存起来
            if(c == 0) adde(u,v,1);
        }
        if(solve()) puts("possible");
        else puts("impossible");
    }
    return 0;
}


//求最小割是否唯一
//判断方法是先做一遍最大流求最小割,然后从源点和汇点分别遍历所有能够到达的点,看是否覆盖了所有的点,如果覆盖了所有的点,那就是唯一的,否则就是不唯一的。

void solve() {
    //先做一遍最大流   
    while(bfs()) dfs(s,INF);
    //分别从起点和终点做一遍bfs
    memset(vis,0,sizeof(vis));
    qs = qe = 0;
    q[qe++] = s;
    vis[s] = true;
    while(qs < qe) {
        int now = q[qs++];
        for(int i = first[now];~i;i = nxt[i]) {
            if(cap[i] && !vis[v[i]]) {
                vis[v[i]] = true; q[qe++] = v[i];
            }
        }
    }
    qs = qe = 0;
    q[qe++] = t;
    vis[t] = true;
    while(qs < qe) {
        int now = q[qs++];
        for(int i = first[now];~i;i = nxt[i]) {
            if(cap[i ^ 1] && !vis[v[i]]) {
                vis[v[i]] = true; q[qe++] = v[i];
            }
        }
    }
    for(int i = 1;i <= n;i++) {
        if(!vis[i]) {
            puts("AMBIGUOUS");
            return;
        }
    }
    puts("UNIQUE");
}

//带容量限制的无源点和汇点的网络流
//建立虚拟的源点和汇点,因为忽略掉了容量下界之后会导致流量不平衡,所以对于每个u,v,u多出来的流量流到汇点,v不够的流量从源点补流


typedef long long LL;
const int maxn = 205;
const int maxm = maxn * maxn;
const int INF = INT_MAX / 3;
int cap[maxn][maxn],flow[maxn][maxn],low[maxm];
int q[maxn],alpha[maxn],pre[maxn];
int uu[maxm],vv[maxm];
int n,m,s,t,qs,qe;

void solve() {
    memset(flow,0,sizeof(flow));
    while(1) {
        qs = qe = 0;
        for(int i = s;i <= t;i++) pre[i] = -2;
        pre[s] = -1; alpha[s] = INF;
        q[qe++] = s;
        while(qs < qe) {
            int now = q[qs++];
            for(int i = s;i <= t;i++) {
                if(cap[now][i] - flow[now][i] != 0 && pre[i] == -2) {
                    q[qe++] = i;
                    pre[i] = now; 
                    alpha[i] = min(alpha[now],cap[now][i] - flow[now][i]);
                }
            }
        }
        if(pre[t] == -2) break;
        for(int i = t;pre[i] != -1;i = pre[i]) {
            flow[pre[i]][i] += alpha[t];
            flow[i][pre[i]] -= alpha[t];
        }
    }
    bool ok = true;
    for(int i = s + 1;i <= t;i++) {
        if(cap[s][i] - flow[s][i]) ok = false;
    }
    for(int i = s;i < t;i++) if(cap[i][t] - flow[i][t]) ok = false;
    if(!ok) puts("NO");
    else {
        puts("YES");
        for(int i = 0;i < m;i++) {
            printf("%d\n",flow[uu[i]][vv[i]] + low[i]);
        }
    }
}

int main() {
    scanf("%d%d",&n,&m);
    s = 0; t = n + 1;
    memset(cap,0,sizeof(cap));
    for(int i = 0;i < m;i++) {
        int u,v,l,c; scanf("%d%d%d%d",&u,&v,&l,&c);
        low[i] = l;
        cap[u][v] += c - l;
        cap[s][v] += l;
        cap[u][t] += l;
        uu[i] = u; vv[i] = v;
    }
    solve();
    return 0;
}

//二分图最大匹配
int dfs(int now) {
    for(int i = 1;i <= cnty;i++) if(g[now][i] && !vis[i]) {
        vis[i] = true;
        if(!by[i] || dfs(by[i])) {
            bx[now] = i; by[i] = now;
            return 1;
        }
    }
    return 0;
}
 
int solve() {
    int ret = 0;
    memset(bx,0,sizeof(bx));
    memset(by,0,sizeof(by));
    for(int i = 1;i <= cntx;i++) if(!bx[i]) {
        memset(vis,0,sizeof(vis));
        ret += dfs(i);
    }
    return ret;
}

//最小点权覆盖->最小割+拆点
//求最大独立集 最大独立点集 = 点数 - 最大匹配数注意
//最小点覆盖 == 最大匹配
//最小路径覆盖 = 顶点数 - 最大匹配

  

posted @ 2014-08-01 12:43  acm_roll  阅读(177)  评论(0编辑  收藏  举报