因为每个点只能经过一次 所以考虑拆点

这题有坑,有重边。。

KM算法

把一个点拆成入点和出点 入点在X部,出点在Y步。

如果u,v之间有路径,就在X部的u点连接Y部的v点

求完美匹配。

当完美匹配的时候,每个点都有一个入度和一个出度,可知成环。

因为完美匹配求得是最大匹配

记得把每条边权值取相反数

#include <iostream>
#include <cstring>
#include <cstdio> using namespace std;
const int MAXN = ;
const int INF = 0x3f3f3f3f; int G[MAXN][MAXN];
int vx[MAXN], vy[MAXN];
bool visx[MAXN], visy[MAXN];
int match[MAXN];
int slack[MAXN]; int N, M; bool dfs(int x)
{
visx[x] = true; for (int y = ; y < N; ++y) { if (visy[y]) continue; int gap = vx[x] + vy[y] - G[x][y]; if (gap == ) {
visy[y] = true;
if (match[y] == - || dfs( match[y] )) {
match[y] = x;
return true;
}
} else {
slack[y] = min(slack[y], gap);
}
} return false;
} int KM()
{
memset(match, -, sizeof match);
memset(vy, , sizeof vy); for (int i = ; i < N; ++i) {
vx[i] = G[i][];
for (int j = ; j < N; ++j) {
vx[i] = max(vx[i], G[i][j]);
}
} for (int i = ; i < N; ++i) { fill(slack, slack + N, INF); while () {
memset(visx, false, sizeof visx);
memset(visy, false, sizeof visy); if (dfs(i)) break; int d = INF;
for (int j = ; j < N; ++j)
if (!visy[j]) d = min(d, slack[j]); for (int j = ; j < N; ++j) {
if (visx[j]) vx[j] -= d;
if (visy[j]) vy[j] += d;
else slack[j] -= d;
}
}
} int res = ;
for (int i = ; i < N; ++i)
res += G[ match[i] ][i]; return res;
} int Scan() {
int res = , flag = ;
char ch;
if((ch = getchar()) == '-') flag = ;
else if(ch >= '' && ch <= '') res = ch - '';
while((ch = getchar()) >= '' && ch <= '')
res = res * + (ch - '');
return flag ? -res : res;
} int main()
{
int T = Scan();
while (T--) {
N = Scan(), M = Scan();
for (int i = ; i <= N; ++i) {
for (int j = ; j <= N; ++j) {
G[i][j] = -INF;
}
}
int u, v, w;
while (M--) {
u = Scan()-, v = Scan()-, w = Scan();
G[u][v] = max(G[u][v], -w);
} printf("%d\n", -KM());
}
return ;
}

费用流

就是把上面的完美匹配用网络流来求。。

和完美匹配一样

如果u,v之间有路径,就u的入点连v的出点,然后所有入点连起点,所有出点连汇点,求最大流最小花费即可。

费用流比起KM慢了几倍。。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <bitset>
#include <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <map>
#include <set>
#define pk(x) printf("%d\n", x)
using namespace std;
#define PI acos(-1.0)
#define EPS 1E-6
#define clr(x,c) memset(x,c,sizeof(x))
//#pragma comment(linker, "/STACK:102400000,102400000") typedef long long ll;
#define CLR(x, v, n) memset(x, v, sizeof(x[0])*n)
const int N = ;
const int M = ;
const int INF = 0x3f3f3f3f; struct Edge {
int to, next, cap, flow, cost;
void init(int _to, int _cap, int _cost, int _next) {
to = _to; cap = _cap; cost = _cost; next = _next; flow = ;
}
} edge[M]; int head[N], cntE;
int pre[N], dis[N];
bool vis[N];
int src, sink, tot; void dn(int &x, int y) { if(x>y) x=y; } void init() {
cntE = ;
memset(head, -, sizeof head);
} void addedge(int u, int v, int cap, int cost) {
edge[cntE].init(v, cap, cost, head[u]); head[u] = cntE++;
edge[cntE].init(u, , -cost, head[v]); head[v] = cntE++;
} bool spfa() {
queue<int> q;
fill(dis, dis+tot, INF); CLR(vis, false, tot); CLR(pre, -, tot);
dis[src] = ; vis[src] = true;
q.push(src);
while (q.size()) {
int u = q.front(); q.pop(); vis[u] = false;
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].cap > edge[i].flow && dis[u]+edge[i].cost < dis[v]) {
dis[v] = dis[u]+edge[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
if (pre[sink] == -) return false;
return true;
} int MCMF() {
int flow = ;
int cost = ;
while (spfa()) {
int f = INF;
for (int i = pre[sink]; ~i; i = pre[edge[i^].to]) {
dn(f, edge[i].cap - edge[i].flow);
}
for (int i = pre[sink]; ~i; i = pre[edge[i^].to]) {
edge[i].flow += f;
edge[i^].flow -= f;
cost += edge[i].cost * f;
}
flow += f;
}
//return flow;
return cost;
} int Scan() {
int res = , flag = ;
char ch;
if((ch = getchar()) == '-') flag = ;
else if(ch >= '' && ch <= '') res = ch - '';
while((ch = getchar()) >= '' && ch <= '')
res = res * + (ch - '');
return flag ? -res : res;
} int n, m;
int G[][];
int main()
{
int T = Scan();
while (T--) {
clr(head, -);
cntE = ;
n = Scan(), m = Scan();
int u, v, w;
src = , sink = n*+, tot = n*+;;
for (int i = ; i <= n; ++i) {
addedge(src, i, , );
addedge(i+n, sink, , );
}
for (int i = ; i <= n; ++i) for (int j = ; j <= n; ++j) G[i][j] = INF;
while (m--) {
u = Scan(), v = Scan(), w = Scan();
G[u][v] = min(G[u][v], w);
}
for (int i = ; i <= n; ++i) for (int j = ; j <= n; ++j) {
if (G[i][j] != INF) {
addedge(i, j+n, , G[i][j]);
}
}
printf("%d\n", MCMF());
}
return ;
}

上面的费用流太慢辣,又找了个快点的。嘿嘿XD

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <bitset>
#include <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <map>
#include <set>
#define pk(x) printf("%d\n", x)
using namespace std;
#define PI acos(-1.0)
#define EPS 1E-6
#define clr(x,c) memset(x,c,sizeof(x))
//#pragma comment(linker, "/STACK:102400000,102400000") typedef long long ll; const int MAXV = ;
const int INF = <<; struct Edge { int to, cap, cost, rev; };
vector<Edge> G[MAXV];
int dist[MAXV], prv[MAXV], pre[MAXV], in[MAXV];
queue<int> que; void addedge(int from, int to, int cap, int cost) {
G[from].push_back((Edge){to, cap, cost, G[to].size()});
G[to].push_back((Edge){from, , -cost, G[from].size()-});
} int min_cost_max_flow(int s, int t) { //, int f) {
int res = ;
int f = ;
while () { //f > 0) {
for (int i = ; i <= t; ++i) dist[i] = INF, in[i] = ;
dist[s] = ;
while (!que.empty()) que.pop();
in[s] = ;
que.push(s); while (!que.empty()) {
int u = que.front(); que.pop(); in[u] = ;
for (int i = ; i < G[u].size(); ++i) {
Edge &e = G[u][i];
if (e.cap > && dist[e.to] > dist[u] + e.cost) {
dist[e.to] = dist[u] + e.cost;
prv[e.to] = u;
pre[e.to] = i;
if (in[e.to] == ) {
in[e.to] = ;
que.push(e.to);
}
}
}
} if (dist[t] == INF) break; //return -1; int d = INF; // d = f;
for (int v = t; v != s; v = prv[v]) {
d = min(d, G[prv[v]][pre[v]].cap);
}
f += d;
res += d * dist[t];
for (int v = t; v != s; v = prv[v]) {
Edge &e = G[prv[v]][pre[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
} int Scan() {
int res = , flag = ;
char ch;
if((ch = getchar()) == '-') flag = ;
else if(ch >= '' && ch <= '') res = ch - '';
while((ch = getchar()) >= '' && ch <= '')
res = res * + (ch - '');
return flag ? -res : res;
} int n, m;
int mp[][];
int main()
{
int T = Scan();
while (T--) {
n = Scan(), m = Scan();
int u, v, w;
int src = , sink = n*+;
for (int i = ; i <= sink; ++i) G[i].clear();
for (int i = ; i <= n; ++i) {
addedge(src, i, , );
addedge(i+n, sink, , );
}
for (int i = ; i <= n; ++i) for (int j = ; j <= n; ++j) mp[i][j] = INF;
while (m--) {
u = Scan(), v = Scan(), w = Scan();
mp[u][v] = min(mp[u][v], w);
}
for (int i = ; i <= n; ++i) for (int j = ; j <= n; ++j) {
if (mp[i][j] != INF) {
addedge(i, j+n, , mp[i][j]);
}
}
printf("%d\n", min_cost_max_flow(src, sink));
}
return ;
}

HDU 3488--Tour(KM or 费用流)的更多相关文章

  1. Hdu 3488 Tour (KM 有向环覆盖)

    题目链接: Hdu 3488 Tour 题目描述: 有n个节点,m条有权单向路,要求用一个或者多个环覆盖所有的节点.每个节点只能出现在一个环中,每个环中至少有两个节点.问最小边权花费为多少? 解题思路 ...

  2. hdoj 3488 Tour 【最小费用最大流】【KM算法】

    Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submi ...

  3. hdu 1533 KM或费用流

    以前用KM写过,现在再用费用流写. #include <iostream> #include <cstdio> #include <cstring> #includ ...

  4. 【进阶——最小费用最大流】hdu 1533 Going Home (费用流)Pacific Northwest 2004

    题意: 给一个n*m的矩阵,其中由k个人和k个房子,给每个人匹配一个不同的房子,要求所有人走过的曼哈顿距离之和最短. 输入: 多组输入数据. 每组输入数据第一行是两个整型n, m,表示矩阵的长和宽. ...

  5. HDU 5644 King's Pilots 费用流

    King's Pilots 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5644 Description The military parade w ...

  6. HDU 3667 Transportation(网络流之费用流)

    题目地址:HDU 3667 这题的建图真是巧妙...为了保证流量正好达到k.须要让每一次增广到的流量都是1,这就须要把每一条边的流量都是1才行.可是每条边的流量并非1,该怎么办呢.这个时候能够拆边,反 ...

  7. HDU - 5406 CRB and Apple (费用流)

    题意:对于给定的物品,求两个在高度上单调不递增,权值上单调不递减的序列,使二者长度之和最大. 分析:可以用费用流求解,因为要求长度和最大,视作从源点出发的流量为2的费用流,建负权边,每个物品只能取一次 ...

  8. 图论(二分图,KM算法):HDU 3488 Tour

    Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submis ...

  9. HDU 3488 Tour (最大权完美匹配)【KM算法】

    <题目链接> 题目大意:给出n个点m条单向边边以及经过每条边的费用,让你求出走过一个哈密顿环(除起点外,每个点只能走一次)的最小费用.题目保证至少存在一个环满足条件. 解题分析: 因为要求 ...

随机推荐

  1. 在linux下,查看一个运行中的程序, 占用了多少内存

    1. 在linux下,查看一个运行中的程序, 占用了多少内存, 一般的命令有 (1). ps aux: 其中  VSZ(或VSS)列 表示,程序占用了多少虚拟内存. RSS列 表示, 程序占用了多少物 ...

  2. Eclipse不能自动编译 java文件的解决方案

    前段时间出现了eclipse 不自动编译java文件的问题,在网上找了好长时间,总算把问题解决了,现在把这个问题的解决方法总结一下. 1,看看project -- Build Automaticall ...

  3. Java Web开发 之小张老师总结中文乱码解决方案

    中文乱码:在以后学习过程中全部采用UTF-8 1.文件的乱码 1.1.项目文本文件默认编码:        [右击项目]->[Properties]->[Resource]->[Te ...

  4. Android 使用split函数进行多个空格分割

    在项目中经常会遇到按字符分割字符串的情况,可以使用String对象的split函数进行分割. 先看实际情况: String str = "关键词1 关键词2 关键词3"; Stri ...

  5. 安装Hadoop系列 — 新建MapReduce项目

    1.新建MR工程 依次点击 File → New → Ohter…  选择 “Map/Reduce Project”,然后输入项目名称:mrdemo,创建新项目:     2.(这步在以后的开发中可能 ...

  6. linux非阻塞的socket EAGAIN的错误处理

    http://blog.csdn.net/tianmohust/article/details/8691644 在Linux中使用非阻塞的socket的情形下. (一)发送时 当客户通过Socket提 ...

  7. movzbl和movsbl

    汇编语言中最最常用的指令 -- 数据传送指令,也是我们接触的第一种类别的汇编指令.其指令的格式为:“mov 源操作数, 目的操作数”.mov系列支持从最小一个字节到最大双字的访问与传送.其中movb用 ...

  8. Android开发之parseSdkContent failed Could not initialize class android.graphics.Typeface

    在进行android开发过程中,忽然发现经常弹出来parseSdkContent failed 这个错误,然后google了下解决办法 方法1: 删除.android文件 重启eclipse. 该方法 ...

  9. jquery网页倒计时效果,秒杀,限时抢购!

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  10. 转 Android adb root权限

    永久root带文件 因为开发需要,我经常会用到adb这个工具(Android Debug Bridge),我们都知道adb shell默认是没有root权限的,修改系统文件就很不方便了,adb pus ...