图算法 |
度限制最小生成树和第K最短路,分数规划 |
poj1639, poj3621, poj2976 poj2449,poj3255,poj2513 |
最短路,最小生成树,二分图,最大流问题的相关理论 |
poj3155,poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446 |
|
最优比率生成树 |
poj2728(0/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时说明得到方程的解。
#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就行。
#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
可以作为这类问题的模板
#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的次短路
改改模板就可以了,不过这里是无向图,要加双向边的。。。
#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