Going Home
题意:n个人,进n个房子,每走一格花费1美元,每个房子只能进一人,求所有人进房子的最小花费。 就是推箱子 箱子最短行走距离
这题无法用bfs做 !
用最小花费最大流
通过EK,Dinic,ISAP算法可以得到网络流图中的最大流,一个网络流图中最大流的流量max_flow是唯一的,但是达到最大流量max_flow时每条边上的流量分配f是不唯一的。
如果给网络流图中的每条边都设置一个费用cost,表示单位流量流经该边时会导致花费cost。那么在这些流量均为max_flow的流量分配f中,存在一个流量总花费最小的最大流方案。
即 min{sum(cost(i, j)*f(i,j) | (i, j)属于方案f中的边, f(i,j)为 边(i,j)上的流量, f为某一个最大流方案}。此即为最小费用最大流。
建图:
超级源点到所有人 每个人到每个房子均算出花费 所有房子到超级汇点 所有的边均为1 花费除了人到房子均为0
可当作最小花费最大流模板:
#include <cstdio>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <algorithm>
#define MAXN 200+10
#define MAXM 80000+100
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N, M;
int cost, flow;
int sink, source;//超级源点 超级汇点
void init()
{
edgenum = ;
memset(head, -, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, , c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, , , -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
int dis(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
struct Node
{
int x, y;
};
Node m[], H[];//存储字符坐标 bool SPFA(int s, int t)//寻找花销最少的路径
{
queue<int> Q;
memset(dist, INF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -, sizeof(pre));
dist[s] = ;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以松弛 且 没有满流
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;//记录前驱边 的编号
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -;//可达返回true
}
void MCMF(int s, int t)
{
flow = ;//总流量
cost = ;//总费用 while(SPFA(s, t))//每次寻找花销最小的路径
{
int Min = INF;
//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
for(int i = pre[t]; i != -; i = pre[edge[i^].to])
{
Edge E = edge[i];
Min = min(Min, E.cap - E.flow);
}
//增广
for(int i = pre[t]; i != -; i = pre[edge[i^].to])
{
edge[i].flow += Min;
edge[i^].flow -= Min;
cost += edge[i].cost * Min;//增广流的花销
}
flow += Min;//流量累加
}
}
int main()
{
int m_cnt;//m字符计数器
int H_cnt;//H字符计数器 while(scanf("%d%d", &N, &M), N||M)
{
init(); m_cnt = H_cnt = ;
char str[][];
for(int i = ; i < N; i++)
{
scanf("%s", str[i]);
for(int j = ; j < M; j++)
{
if(str[i][j] == 'm')
{
++m_cnt;
m[m_cnt].x = i;
m[m_cnt].y = j;
}
if(str[i][j] == 'H')
{
++H_cnt;
H[H_cnt].x = i;
H[H_cnt].y = j;
}
}
}
int k = m_cnt;//人数 或者 房子数
sink = ;
source = *k+;
for(int i = ; i <= k; i++)
{
addEdge(sink, i, , );
addEdge(i + k, source, , );
for(int j = ; j <= k; j++)
{
int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
addEdge(i, j + k, , d);
}
} MCMF(sink, source);
printf("%d\n", cost);
}
return ;
}
随机推荐
- 【CF771A】Bear and Friendship Condition
题目大意:给定一张无向图,要求如果 A 与 B 之间有边,B 与 C 之间有边,那么 A 与 C 之间也需要有边.问这张图是否满足要求. 题解:根据以上性质,即:A 与 B 有关系,B 与 C 有关系 ...
- token的理解
今天学习了token,它的英文意思是令牌的意思.在我理解即像通行证一样,在用户登录成功系统后,会为这个用户颁发一个token,这样它去其他系统都免登录,因为有了这个令牌. token的生成我们可以用U ...
- 使用 EXISTS 代替 IN 和 inner join
在使用Exists时,如果能正确使用,有时会提高查询速度: 1,使用Exists代替inner join 2,使用Exists代替 in 1,使用Exists代替inner join例子: 在一般写s ...
- java生成扑克牌----java基础学习总结
前言都懒的写了,都凌晨1点半了,直接把代码放上去 代码: package com.day16.list; import java.util.LinkedList; import java.util.R ...
- win7下PLSQL Developer提示“ORA-12154: TNS:无法解析指定的连接标识符”
解决方法:卸载掉重新安装,注意安装的目录的文件夹不要有特殊的符号,例如64位系统的的安装目录会到Program Files (x86),这时候就会出现"ORA-12154: TNS:无法解析 ...
- swift学习笔记3
1.在 Swift 中,枚举类型是一等(first-class)类型.它们采用了很多在传统上只被类(class)所支持的特性,例如计算型属性(computed properties),用于提供枚举值的 ...
- Linux 静态库与动态库
静态库(.a) 一个deal.c usedeal.c 重点 1. gcc -c deal.c 生成 deal.o 2. ar -rsv libdeal.a deal.o 生成 libdeal.a ...
- 20155206 2016-2017-2 《Java程序设计》第8周学习总结
20155206 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十五章 通用API 15.1 日志 日志API简介 java.util.logging包提 ...
- json 删除、添加对象
1. 定义json对象 var entryJson = []; 2. 删除.添加对象 entryJson.pop(); //删除最后一个对象 entryJson.push({ //往 ...
- 【C++】wchar、char格式化符输出
VC.BCB.MinGW Linux下的GCC.C99标准 printf wprintf printf wprintf s char wchar_t char S wchar_t char * hs ...