图论训练

1.POJ 3635

使用dijkstra堆优化思想,状态为二维的,每个点的油箱里的油,求每个状态的最小花费,每次从堆中弹出的状态就确定了这种状态的最小花费。

对于每个状态,两种拓展方式,加一单位油,和直接向下开。

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
#define MAXN 300050
#define INF 0x3fffffff

int tot;
int head[MAXN], vis[MAXN][102];
int dis[MAXN][102];
int T, n, m;
struct Edge {
    int to, nxt;
    int cap;
} edge[MAXN * 3];
int add_oil[MAXN];
void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
    edge[tot].to = v;
    edge[tot].cap = w;
    edge[tot].nxt = head[u];
    head[u] = tot++;
}

struct point {
    int v, cost, oil;
    point(int a, int b, int c) {
        v = a, cost = b, oil = c;
    }
    bool operator <(const point &tmp) const {
        return cost > tmp.cost;
    }
};
int dijkstra(int c, int s, int e) {
    priority_queue<point> q;
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= c; j++)
            dis[i][j] = INF, vis[i][j] = 0;
    dis[s][0] = 0;
    q.push(point(s, 0, 0));
    while (!q.empty()) {
        point cur = q.top();
        q.pop();
        if (cur.v == e)
            return cur.cost;
        if (vis[cur.v][cur.oil])
            continue;
        vis[cur.v][cur.oil] = 1;
        if (cur.oil < c && !vis[cur.v][cur.oil + 1]
                && dis[cur.v][cur.oil + 1] > cur.cost + add_oil[cur.v]) {
            dis[cur.v][cur.oil + 1] = cur.cost + add_oil[cur.v];
            q.push(point(cur.v, cur.cost + add_oil[cur.v], cur.oil + 1));
        }
        for (int i = head[cur.v]; i != -1; i = edge[i].nxt) {
            int v = edge[i].to;
            if (v == cur.v || cur.oil < edge[i].cap)
                continue;
            if (!vis[edge[i].to][cur.oil]
                    && dis[edge[i].to][cur.oil - edge[i].cap] > cur.cost) {
                dis[edge[i].to][cur.oil - edge[i].cap] = cur.cost;
                q.push(point(edge[i].to, cur.cost, cur.oil - edge[i].cap));
            }
        }
    }
    return -1;
}
int main() {
//    freopen("data4.txt", "r", stdin);
    int u, v, w, q;
    int c, s, e;
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 0; i < n; i++)
            scanf("%d", &add_oil[i]);
        init();
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
            addedge(v, u, w);
        }
        scanf("%d", &q);
        while (q--) {
            scanf("%d%d%d", &c, &s, &e);
            int ans = dijkstra(c, s, e);
            if (ans == -1)
                puts("impossible");
            else
                printf("%d\n", ans);
        }
    }
    return 0;
}

 2.POJ 3621

这个方法用来求图论中的“最优比例环”,这种方法可以认为是导出一个单调的函数通过二分求这个最优比例的方法。

01分数规划,又称参数搜索。即求形如(∑Di*xi)/(∑Ci*xi)的最值问题,其中xi={0,1}即只有取或不取2种状态。设最值为ans则必有∑(Di-ans*Ci)*xi=0,且因为Di和Ci都是给定的,当xi确定时,等式左边可以看成是一个以ans为变量的单调函数。因此求最优解转化为求解方程跟的问题。常见的有:最优比率生成树,最优比率环。

(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)=L,则我们可以将此式转换为:x1*(a1-b1*L)+x2*(a2-b2*L)+...xn*(an-bn*L)=0,我们先定义一个估计值val,如果这个值使得上面的式子小于0我们就可以知道val>L,如果上式等于0,则val = L;如果大于0,则val<L,显然我们可以采用二分的思想求解次问题。

假设存在边uv,点u和点v的欢乐值分别为happy[u]和happy[v]。u到v的花费为cost[u][v]。则我们可以构造一个新图,这个新图的边变为:
happy[v]-val*cost[u][v](val为估计值),然后我们采用SPFA求解此图是否存在负环。如果存在负环。假设此环所有点为y1,y2.....ym,则满足下面式子:
(cost[1][2]*val-happ[2])+(cost[2][3]*val-happy[3])+....+(cost[m][1]-happ[1]) < 0
显然val比最优解还要小,此时我们可以增大val,反之,如果不存在负环,则我们需要减少val(对于a1和b1*L在符号中的先后关系需要看题目是求最大值还是最小值,此题是把b1*L放在符号前面的)。如此进行二分求解,知道满足题目要求的精度就可以终止了。 对于POJ 2728 则可以使用类似的方法求解.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
#define MAXN 1009
#define INF 0x3fffffff
#define eps 1e-3
int n, m;
int FUN[MAXN];
int tot, head[MAXN];
struct Edge {
    int v, next;
    double c, w;
} edge[5005];
void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, double c) {
    edge[tot].v = v;
    edge[tot].c = c;
    edge[tot].next = head[u];
    head[u] = tot++;
}
double dis[MAXN];
int cnt[MAXN], vis[MAXN];
bool checked[MAXN];
bool spfa(int s, double mid) {
    queue<int> Q;
    for (int i = 1; i <= n; i++)
        dis[i] = (double) INF, vis[i] = cnt[i] = 0;
    dis[s] = 0;
    vis[s] = checked[s] = 1;
    Q.push(s);
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;
        checked[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            edge[i].w = mid * edge[i].c - FUN[edge[i].v];
            if (dis[u] + edge[i].w < dis[v]) {
                dis[v] = dis[u] + edge[i].w;
                if (!vis[v]) {
                    Q.push(v);
                    vis[v] = 1;
                    if (++cnt[v] > n)
                        return 0;
                }
            }
        }
    }
    return 1;
}
bool check(double mid) {
    for (int i = 1; i <= n; i++)
        if (!checked[i] && !spfa(i, mid))
            return 0;
    return 1;
}
int main() {
//    freopen("data4.txt", "r", stdin);
    int u, v, w;
    while (~scanf("%d%d", &n, &m)) {
        init();
        for (int i = 1; i <= n; i++)
            scanf("%d", &FUN[i]);
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
        }
        double low = 0, high = 100.0, mid;
        while (high - low > eps) {
            mid = (low + high) / 2;
            memset(checked, 0, sizeof(checked));
            if (check(mid))
                high = mid;
            else
                low = mid;
        }
        if (fabs(low - 1000.0) < eps)
            puts("0");
        else
            printf("%.2lf\n", low);
    }
    return 0;
}

 3.HDU 4786 生成树

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAXN 100009

int N, M;
struct Edge {
    int u, v, w;
    bool operator <(const Edge &tmp) const {
        return w < tmp.w;
    }
} edge[MAXN * 2];
int father[MAXN];
void init() {
    for (int i = 1; i <= N; i++)
        father[i] = i;
}
int getfa(int x) {
    if (x == father[x])
        return x;
    return father[x] = getfa(father[x]);
}
int func(int d) {
    init();
    int i = (d == 1) ? 0 : M - 1;
    int cnt = 0;
    for (; i >= 0 && i < M; i += d) {
        if (getfa(edge[i].u) != getfa(edge[i].v)) {
            father[father[edge[i].u]] = father[edge[i].v];
            cnt += edge[i].w;
        }
    }
    int p = 0;
    for (i = 1; i <= N; i++)
        if (i == father[i])
            p++;
    if (p == 1)
        return cnt;
    return -1;
}
bool benum[MAXN];
int main() {
//    freopen("data3.txt", "r", stdin);
    memset(benum, 0, sizeof(benum));
    int a[2] = { 1, 2 }, tmp;
    benum[1] = benum[2] = 1;
    while (1) {
        tmp = a[1];
        a[1] = a[0] + a[1];
        a[0] = tmp;
        if (a[1] > MAXN)
            break;
        benum[a[1]] = 1;
    }
    int T;
    scanf("%d", &T);
    for (int cas = 1; cas <= T; cas++) {
        printf("Case #%d: ", cas);
        scanf("%d%d", &N, &M);
        for (int i = 0; i < M; i++)
            scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
        sort(edge, edge + M);
        int low = func(1), high;
        if (low == -1) {
            puts("No");
            continue;
        }
        high = func(-1);
        int j;
        for (j = low; j <= high; j++)
            if (benum[j])
                break;
        if (j > high)
            puts("No");
        else
            puts("Yes");
    }
    return 0;
}

 4.约束差分系统

/*
 * 在区间[0,50000]上有一些整点,并且满足n个约束条件(u, v, w),即在区间[u, v]上至少有x个整点,问区间[0, 50000]上至少有几个整点。
 * 构造差分约束系统的关键:用dict[i]表示区间[0, i]上的整点数,则约束条件可化为dict[v] - dict[u-1] >= w,为spfa求最长路径
 * 另外还必须挖掘完整两个隐含的约束条件,0 <= dict[i] - dict[i-1] <= 1
 */
#include<iostream>
#include<queue>
using namespace std;
const int MAXN = 50005;
#define INF 0x3fffffff
int low, high;
int tot;
int head[MAXN];
struct Edge {
    int v, w;
    int next;
} edge[MAXN * 4];
int dis[MAXN];
bool vis[MAXN];
void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int spfa() { // 求最长路
    queue<int> Q;
    for (int i = low; i <= high; i++) {
        dis[i] = -INF;
        vis[i] = 0;
    }
    dis[low] = 0;
    vis[low] = 1;
    Q.push(low);
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if (dis[u] + edge[i].w > dis[v]) {
                dis[v] = dis[u] + edge[i].w;
                if (!vis[v]) {
                    Q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return dis[high];
}
int main() {
//    freopen("data3.txt", "r", stdin);
    int ai, bi, ci, n;
    while (~scanf("%d", &n)) {
        init();
        low = INF;
        high = -1;
        for (int i = 0; i < n; i++) {
            scanf("%d %d %d", &ai, &bi, &ci);
            ai++, bi++;
            low = min(low, ai - 1);
            high = max(high, bi);
            addedge(ai - 1, bi, ci);
        }
        for (int i = low + 1; i <= high; i++) {
            addedge(i - 1, i, 0);
            addedge(i, i - 1, -1);
        }
        printf("%d\n", spfa());
    }
    return 0;
}

 5.POJ 1364

不连通要设置个源点,这样实现对所有点的负环检验,检验条件是被松弛了大于点个数次,这题有n+1个点

#include<iostream>
#include<queue>
using namespace std;
const int MAXN = 50005;
#define INF 0x3fffffff
int tot;
int n;
int head[MAXN];
struct Edge {
    int v, w;
    int next;
} edge[MAXN * 4];
int dis[MAXN], cnt[MAXN];
bool vis[MAXN];
void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int spfa() { // 求最长路
    queue<int> Q;
    for (int i = 0; i <= n + 2; i++) {
        dis[i] = -INF;
        vis[i] = cnt[i] = 0;
    }
    dis[n + 1] = 0;
    vis[n + 1] = 1;
    cnt[n + 1]++;
    Q.push(n + 1);
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if (dis[u] + edge[i].w > dis[v]) {
                dis[v] = dis[u] + edge[i].w;
                cnt[v]++;
                if (cnt[v] > n + 1)
                    return 0;
                if (!vis[v]) {
                    Q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return 1;
}
int main() {
//    freopen("data2.txt", "r", stdin);
    int ai, bi, ci, m;
    char OP[5];
    while (scanf("%d", &n) && n) {
        scanf("%d", &m);
        init();
        while (m--) {
            scanf("%d%d%s%d", &ai, &bi, OP, &ci);
            if (OP[0] == 'g')
                addedge(ai - 1, ai + bi, ci + 1);
            else
                addedge(ai + bi, ai - 1, 1 - ci);
        }
        for (int i = 0; i <= n; i++)
            addedge(n + 1, i, 0);
        if (!spfa())
            puts("successful conspiracy");
        else
            puts("lamentable kingdom");
    }
    return 0;
}

 6.HDU 3026

对最小生成树的深入理解题 先BFS求两点间最小代价,然后一遍最小生成树的变形,因为记录的代价就是到根结点的

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
int map[51][51];
int cost[104][104];
bool vis[51][51];
int cnt;
int X, Y;
struct Node {
    int x, y, step;
    Node() {
        step = 0;
    }
    Node(int _x, int _y) :
            x(_x), y(_y) {
        step = 0;
    }
} start, record[104];
int dir[4][2] = { -1, 0, 0, -1, 0, 1, 1, 0 };
inline bool check(Node t) {
    return t.x >= 0 && t.y >= 0 && t.x < X && t.y < Y;
}
void BFS(Node start) {
    memset(vis, 0, sizeof(vis));
    queue<Node> Q;
    Q.push(start);
    vis[start.x][start.y] = 1;
    Node cur, tmp;
    int cnt_t = cnt;
    while (!Q.empty()) {
        cur = Q.front();
        Q.pop();
        for (int i = 0; i < 4; i++) {
            tmp.x = cur.x + dir[i][0];
            tmp.y = cur.y + dir[i][1];
            tmp.step = cur.step + 1;
            if (check(tmp) && !vis[tmp.x][tmp.y] && map[tmp.x][tmp.y] != -1) {
                if (map[tmp.x][tmp.y] > 0) {
                    cost[map[start.x][start.y]][map[tmp.x][tmp.y]] = tmp.step;
                    cnt_t--;
                    if (!cnt_t)
                        return;
                }
                vis[tmp.x][tmp.y] = 1;
                Q.push(tmp);
            }
        }
    }
}
int dis[104];
int Prim(Node start) {
    int ans = 0;
    bool _vis[104];
    dis[map[start.x][start.y]] = 0;
    for (int i = 1; i < cnt; i++) {
        dis[i] = cost[0][i];
        _vis[i] = 0;
    }
    _vis[0] = 1;
    int Min = 2139062143, p;
    for (int i = 1; i < cnt; i++) {
        Min = 2139062143;
        for (int j = 1; j < cnt; j++)
            if (!_vis[j] && Min > dis[j]) {
                Min = dis[j];
                p = j;
            }
        ans += Min;
        _vis[p] = 1;
        for (int j = 1; j < cnt; j++)
            if (!_vis[j] && dis[j] > cost[p][j])
                dis[j] = cost[p][j];
    }
    return ans;
}
int main() {
//    freopen("data3.txt", "r", stdin);
    int T;
    char t;
    scanf("%d", &T);
    while (T--) {
        memset(map, 0, sizeof(map));
        memset(cost, 0x7f, sizeof(cost));
        scanf("%d%d", &Y, &X);
        char c = getchar(); //WA点,没道理
        while (c == ' ')
            c = getchar();
        cnt = 1;
        for (int i = 0; i < X; i++) {
            for (int j = 0; j < Y; j++) {
                scanf("%c", &t);
                if (t == '#')
                    map[i][j] = -1;
                else if (t == 'A') {
                    record[cnt] = Node(i, j);
                    map[i][j] = cnt++;
                } else if (t == 'S') {
                    map[i][j] = 0;
                    start.x = i, start.y = j;
                }
            }
            getchar();
        }
        BFS(start);
        Node tmp = start;
        for (int i = 1; i < cnt; i++) {
            BFS(record[i]);
        }
        printf("%d\n", Prim(start));
    }
    return 0;
}

 

posted @ 2013-11-12 16:47  匡时@下一站.info  阅读(247)  评论(0编辑  收藏  举报