现在的位置: 首页 > 综合 > 正文

hdu 1853 费用流

2012年09月25日 ⁄ 综合 ⁄ 共 3157字 ⁄ 字号 评论关闭

找出若干个环覆盖所有的点,使得总的花费最小

因为每个点只能经过一次,所以很快就可想到拆点求最小费用流

建图:

S->i  费用为0 流量为1

i+n->T同上

若有边u->v

u->v+n 费用为边权,容量为1

最后套套模板求一次最小费用流,如果流量等于n,表示每个点都遍历了一次,输出最小费用即可

View Code

#include <iostream>
#include <algorithm>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int sumFlow;
const int MAXN = 502;
const int MAXM = 10002;
const int INF = 1000000000;
struct Edge
{
int u, v, cap, cost;
int next;
}edge[MAXM<<2];
int NE;
int head[MAXN], dist[MAXN], pp[MAXN];
bool vis[MAXN];
void init()
{
NE = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int cap, int cost)
{
edge[NE].u = u; edge[NE].v = v; edge[NE].cap = cap; edge[NE].cost = cost;
edge[NE].next = head[u]; head[u] = NE++;
edge[NE].u = v; edge[NE].v = u; edge[NE].cap = 0; edge[NE].cost = -cost;
edge[NE].next = head[v]; head[v] = NE++;
}
bool SPFA(int s, int t, int n)
{
int i, u, v;
queue <int> qu;
memset(vis,false,sizeof(vis));
memset(pp,-1,sizeof(pp));
for(i = 0; i <= n; i++) dist[i] = INF;
vis[s] = true; dist[s] = 0;
qu.push(s);
while(!qu.empty())
{
u = qu.front(); qu.pop(); vis[u] = false;
for(i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
if(edge[i].cap && dist[v] > dist[u] + edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
pp[v] = i;
if(!vis[v])
{
qu.push(v);
vis[v] = true;
}
}
}
}
if(dist[t] == INF) return false;
return true;
}
int MCMF(int s, int t, int n) // minCostMaxFlow
{
int flow = 0; // 总流量
int i, minflow, mincost;
mincost = 0;
while(SPFA(s, t, n))
{
minflow = INF + 1;
for(i = pp[t]; i != -1; i = pp[edge[i].u])
if(edge[i].cap < minflow)
minflow = edge[i].cap;
flow += minflow;
for(i = pp[t]; i != -1; i = pp[edge[i].u])
{
edge[i].cap -= minflow;
edge[i^1].cap += minflow;
}
mincost += dist[t] * minflow;
}
sumFlow = flow; // 题目需要流量,用于判断
return mincost;
}
int main()
{
int n, m;
int u, v, c;
while (scanf("%d%d", &n, &m) != -1)
{
init();
int S = 0;
int T = n + n + 1;
for (int i = 1; i <= n; ++i)
{
addedge(S, i, 1, 0);
addedge(i + n, T, 1, 0);
}
while (m--)
{
scanf("%d%d%d", &u, &v, &c);
addedge(u, v + n, 1, c);
}
int ans = MCMF(S, T, T+1);
if (sumFlow != n) printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}

另外也可以用二分图匹配中的KM算法来搞

View Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MIN(a,b) ((a)<(b)?(a):(b))
#define INF 999999
#define MAX 5110
int n,match[MAX];
bool sx[MAX],sy[MAX];
int lx[MAX],ly[MAX],map[MAX][MAX];
bool path(int u)
{
sx[u]=true;
for(int v=0;v<n;v++)
if(!sy[v]&&lx[u]+ly[v]==map[u][v])
{
sy[v]=true;
if(match[v]==-1||path(match[v]))
{
match[v]=u;
return true;
}
}
return false;
}
int KM(bool truth)//可以不用更改地处理最小或最大权匹配
{
int i,j;
if(!truth)
{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map[i][j]=-map[i][j];
}
for(i=0;i<n;i++)
{
lx[i]=-INF;
ly[i]=0;
for(j=0;j<n;j++)
if(lx[i]<map[i][j])
lx[i]=map[i][j];
}
memset(match,-1,sizeof(match));
for(int u=0;u<n;u++)
while(1)
{
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
if(path(u)) break;
int dmin=INF;
for(i=0;i<n;i++)
if(sx[i])
for(j=0;j<n;j++)
if(!sy[j])
dmin=MIN(lx[i]+ly[j]-map[i][j],dmin);
for(i=0;i<n;i++)
{
if(sx[i])
lx[i]-=dmin;
if(sy[i])
ly[i]+=dmin;
}
}
int sum=0;
for(j=0;j<n;j++)
sum+=map[match[j]][j];
if(!truth)
{
sum=-sum;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map[i][j]=-map[i][j];
}
return sum;
}
void Map_Init(int m,int n)
{
int x,y,w;
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map[i][j]=INF;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&x,&y,&w);
if(map[x-1][y-1]>w)
map[x-1][y-1]=w;
}
}
int main()
{
int m,i,j,a,b,c;
while(scanf("%d%d",&n,&m)!=EOF)
{
Map_Init(m,n);
int ans=KM(false);
if(ans<INF) printf("%d\n",ans);
else printf("-1\n");
}
}

抱歉!评论已关闭.