POJ 图算法(3)

图算法

度限制最小生成树和第K最短路,分数规划

poj1639, poj3621, poj2976

poj2449poj3255poj2513

最短路,最小生成树,二分图,最大流问题的相关理论

poj3155,poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446

最优比率生成树

poj27280/1分数规划应用)

最小树形图

poj3164(-刘算法)

次小生成树

poj1679(存在O(n^2)DP解法)

2-SAT问题

poj3207, poj3678, poj3683,poj3648, poj2723, poj2749

无向图、有向图的最小环

poj1734(floyd扩展)

 

 

 

 

 

 

 

 

 

 

 

 

 

度限制最小生成树和第K最短路,分数规划  

 

 

poj 1639

标准的最小度限制生成树,详见:http://www.cnblogs.com/vongang/archive/2012/07/03/2575383.html

 

poj  2976

很裸的分数规划,给n个数对,a[i],b[i]可以从中删掉k个数对,然后使得sum(a)/sum(b)最大。

很经典的分数规划问题。k = a(x)/b(x),转化成a(x) - k*b(x),对a(x) - k*b(x)进行排序,删掉前k小的数,然后求和。

当求和为0时说明得到方程的解。

 

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const double eps = 1e-8;
typedef long long LL;
using namespace std;

const int N = (1<<10);

double f[N], g[N];
double G[N];
int dbcmp(double x) {
    if(x > eps) return 1;
    else if(x < -eps)   return -1;
    return 0;
}

int main() {
    //freopen("data.in", "r", stdin);

    int n, k, i;
    while(~scanf("%d%d", &n, &k)) {
        if(n + k == 0)  break;
        REP(i, n)   scanf("%lf", f + i);
        REP(i, n)   scanf("%lf", g + i);
        double sum, l = 0, r = 1;
        while(dbcmp(r - l) > 0) {
            double m = (l + r)/2;
            REP(i, n)   G[i] = f[i] - g[i]*m;
            sort(G, G + n);
            for(sum = 0, i = k; i < n; ++i)  sum += G[i];
            if(dbcmp(sum) > 0)  l = m;
            else    r = m;
        }
        printf("%d\n", int(r*100 + 0.5));
    }
    return 0;
}

 

 poj 3621

题意:给一个无向图,找到一个环,使得环上的 点权之和/边权之和 最大。

思路:这个还真不好想到。。。看的题解。sum(f[i])/sum(g[i][j]) == ans; 展开变形:

g[1][2]* ans - f[1] + g[2][3]*ans + f[2] + ... = 0;

这样就可以把g[i][j]*ans - f[i]当成一条边的另一个权值。二分ans的值,如果有一个环按这个权值计算出现负环,那么说明g[1][2]* ans - f[1] + g[2][3]*ans + f[2] + ... < 0 显然ans要增加,反之减少。

注意g[i][j]*ans - f[i] 是double类型,在这里wa了好几次。。。。

ans的上限定为1000就行。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const double eps = 1e-4;
typedef long long LL;
using namespace std;

const int N = 1024;
const double inf = ~0u>>1;

struct node {
    int from;
    int to;
    int val;
    int next;
} g[5100];

int head[N], t;
int cnt[N], f[N];
double dis[N];
bool inq[N];
int q[1000000];

int n, m;

void add(int u, int v, int w) {
    g[t].from = u; g[t].to = v; g[t].val = w;
    g[t].next = head[u]; head[u] = t++;
}

int dbcmp(double x) {
    if(x > eps) return 1;
    else if(x < -eps)   return -1;
    return 0;
}

bool spfa(double ans) {
    int u, v, i;
    double w;
    int l = 0, r = 0;
    for(i = 1; i <= n; ++i) {
        dis[i] = inf; cnt[i] = 0; inq[i] = false;
    }
    q[r++] = 1; dis[1] = 0;
    inq[1] = true; cnt[1]++;


    while(l < r) {
        u = q[l++];
        for(i = head[u]; i != -1; i = g[i].next) {
            v = g[i].to; w = g[i].val*ans - f[g[i].from];
            if(dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if(!inq[v]) {
                    cnt[v] ++ ;
                    if(cnt[v] > n)  return false;
                    inq[v] = true;
                    q[r++] = v;
                }
            }
        }
        inq[u] = false;
    }
    return true;
}


int main() {
    //freopen("data.in", "r", stdin);

    int i, x, y, z;
    while(~scanf("%d%d", &n, &m)) {
        CL(head, -1); t = 0;
        CL(f, 0);
        FOR(i, 1, n)    scanf("%d", f + i);
        FOR(i, 1, m)  {
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
        }
        double l = 0, r = 100, mid;
        while(dbcmp(r - l) > 0) {
            mid = (l + r)/2;
            if(spfa(mid))   r = mid;
            else    l = mid;
        }
        printf("%.2f\n", r);
    }
    return 0;
}

 

poj 2449

裸第k短路:表示没有接触过A*,看论文看的眼冒金星。启发函数好神奇。。。。

详见:http://www.cnblogs.com/vongang/archive/2012/07/17/2594737.html

可以作为这类问题的模板

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const double eps = 1e-4;
typedef long long LL;
using namespace std;

const int N = 1024;
const int M = 100010;
const int inf = ~0u>>2;

struct edg {
    int to;
    int val;
    int next;
    edg() {}
    edg(int a, int b, int c): to(a), val(b), next(c) {}
} g[M<<1], rg[M<<1];

struct node {
    int f, g, v;
    node() {}
    node(int a, int b, int c) : f(a), g(b), v(c) {}

    bool operator < (const node& x) const {
        return x.f < f;
    }
};

int inq[N];
int head[N];
int rhead[N];
int dis[N];
int t, k;

void init() {
    CL(head, -1);
    CL(rhead, -1);
    CL(inq, 0);
    t = 0;
    for(int i = 0; i < N; ++i)  dis[i] = inf;
}

void add(int u, int v, int w) {
    g[t] = edg(v, w, head[u]);
    rg[t] = edg(u, w, rhead[v]);
    head[u] = t;
    rhead[v] = t++;
}

void spfa(int ed) {
    int i, u, v, w;
    queue<int> q;
    q.push(ed);
    inq[ed] = 1;
    dis[ed] = 0;

    while(!q.empty()) {
        u = q.front();

        for(i = rhead[u]; i != -1; i = rg[i].next) {
            v = rg[i].to; w = rg[i].val;
            if(dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if(!inq[v]) { inq[v] = 1; q.push(v);}
            }
        }
        inq[u] = 0; q.pop();
    }
}

int A_star(int st, int ed) {
    priority_queue<node> Q;
    if(dis[st] == inf)  return -1;
    int v, w;
    CL(inq, 0);
    Q.push(node(dis[st], 0, st));

    while(!Q.empty()) {
        node cur = Q.top();
        Q.pop(); inq[cur.v] ++;
        if(inq[ed] == k)    return cur.f;
        if(inq[cur.v] > k)  continue;

        for(int i = head[cur.v]; i != -1; i = g[i].next) {
            v = g[i].to; w = g[i].val;
            node New(dis[v] + cur.g + w, cur.g + w, v);
            Q.push(New);
        }
    }

    return -1;
}

int main() {
    //freopen("data.in", "r", stdin);

    int n, m, i;
    int u, v, w;
    int st, ed;
    init();
    scanf("%d%d", &n, &m);
    for(i = 0; i < m; ++i) {
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
    }
    scanf("%d%d%d", &st, &ed, &k);

    spfa(ed);
    if(st == ed)    k++;
    printf("%d\n", A_star(st, ed));
    return 0;
}

 

POJ 3255

题意是求1到n的次短路

改改模板就可以了,不过这里是无向图,要加双向边的。。。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const double eps = 1e-4;
typedef long long LL;
using namespace std;

const int N = 5024;
const int M = 100010;
const int inf = ~0u>>2;

struct edg {
    int to;
    int val;
    int next;
    edg() {}
    edg(int a, int b, int c): to(a), val(b), next(c) {}
} g[M<<1], rg[M<<1];

struct node {
    int f, g, v;
    node() {}
    node(int a, int b, int c) : f(a), g(b), v(c) {}

    bool operator < (const node& x) const {
        return x.f < f;
    }
};

int inq[N];
int head[N];
int rhead[N];
int dis[N];
int t, k;

void init() {
    CL(head, -1);
    CL(rhead, -1);
    CL(inq, 0);
    t = 0;
    for(int i = 0; i < N; ++i)  dis[i] = inf;
}

void add(int u, int v, int w) {
    g[t] = edg(v, w, head[u]);
    rg[t] = edg(u, w, rhead[v]);
    head[u] = t;
    rhead[v] = t++;

    g[t] = edg(u, w, head[v]);
    rg[t] = edg(v, w, rhead[u]);
    head[v] = t;
    rhead[u] = t++;
}

void spfa(int ed) {
    int i, u, v, w;
    queue<int> q;
    q.push(ed);
    inq[ed] = 1;
    dis[ed] = 0;

    while(!q.empty()) {
        u = q.front();

        for(i = rhead[u]; i != -1; i = rg[i].next) {
            v = rg[i].to; w = rg[i].val;
            if(dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if(!inq[v]) { inq[v] = 1; q.push(v);}
            }
        }
        inq[u] = 0; q.pop();
    }
}

int A_star(int st, int ed) {
    priority_queue<node> Q;
    if(dis[st] == inf)  return -1;
    int v, w;
    CL(inq, 0);
    Q.push(node(dis[st], 0, st));

    while(!Q.empty()) {
        node cur = Q.top();
        Q.pop(); inq[cur.v] ++;
        if(inq[ed] == k)    return cur.f;
        if(inq[cur.v] > k)  continue;

        for(int i = head[cur.v]; i != -1; i = g[i].next) {
            v = g[i].to; w = g[i].val;
            node New(dis[v] + cur.g + w, cur.g + w, v);
            Q.push(New);
        }
    }

    return -1;
}

int main() {
    //freopen("data.in", "r", stdin);

    int n, m, i;
    int u, v, w;
    int st, ed;
    init();
    scanf("%d%d", &n, &m);
    for(i = 0; i < m; ++i) {
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w);
    }
    st = 1; ed = n; k = 2;

    spfa(ed);
    if(st == ed)    k++;
    printf("%d\n", A_star(st, ed));
    return 0;
}

 

最优比率生成树

POJ 2728

一个典故:2005年杭州赛区。。。楼教高速AC此题,打乱了赛场节奏,顺利夺冠。。。

最优比例生成树模板题,详见:http://www.cnblogs.com/vongang/archive/2012/07/17/2596441.html

 

最小树形图 

POJ 3164 && HDU 2121

http://www.cnblogs.com/vongang/archive/2012/07/18/2596851.html

次小生成树 

POJ 1679

http://www.cnblogs.com/vongang/archive/2012/07/18/2597739.html

 

2-SAT问题 

POJ 2-SAT六题 :http://www.cnblogs.com/vongang/archive/2012/02/16/2353770.html 

 

无向图、有向图的最小环 

POJ 1734

http://www.cnblogs.com/vongang/archive/2012/07/18/2598167.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2012-07-02 19:11  AC_Von  阅读(512)  评论(0)    收藏  举报