题意: 给出一个地图, 1为出口, 0为可以通过的点, 除了出口外其他的点都能站多个人, 出口每秒只能通过1个人, 问在最优的安排下, 所有人都逃生的最短时间和是多少.(不存在有被出口围住的出口的情况)
思路: 费用流.
源点到每个人连流量为1费用为0的边,
每个门按照时刻拆分成n+m+p个门(最差情况下, 一个人逃生的最长时间就是n+m+p),
每个人按照他最快到门的时间向那个门连流量为1, 费用为距离的边,
每个门的当前时刻向下一时刻连流量为inf费用为1的边, 代表所有在门口等待的人都能再花费1走到下一秒的这门.
每个门向汇点连流量为1费用为0的边, 代表每一秒一个门只能出一个人.
代码:
#include<bits/stdc++.h>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
typedef long long ll;
const int M=1e6+5;
const int inf=1e9+5;
const int mod=1e9+7;
char mp[105][105];
vector<pair<int,int> >door;
struct node {
int x,y,pwr;
} pep[M];
int caldist(int x1,int y1,int x2,int y2) {
return abs(x1-x2)+abs(y1-y2);
}
struct node1 {
int to, next, cap, flow, cost;
int x, y;
} edge[M];
int head[M], tol;
int pre[M], dis[M];
bool vis[M];
void init() {
tol = 0;
memset(head, -1, sizeof(head));
}
//左端点,右端点,容量,花费
void addedge(int u, int v, int cap, int cost) {
edge[tol].to = v, edge[tol].cap = cap;
edge[tol].cost = cost, edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u, edge[tol].cap = 0;
edge[tol].cost = -cost, edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s, int t) {
queue<int>q;
for (int i = 0; i<M; i++) {
dis[i] = inf;
vis[i] = 0;
pre[i] = -1;
}
dis[s] = 0, vis[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].cap>edge[i].flow &&
dis[v]>dis[u] + edge[i].cost) {
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
q.pop();
vis[u] = 0;
}
if (pre[t] == -1)
return 0;
return 1;
}
//返回的是最大流, cost存的是最小费用
int minCostMaxflow(int s, int t, int &cost) {
int flow = 0;
cost = 0;
while (spfa(s, t)) {
int Min = inf;
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
if (Min>edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
edge[i].flow += Min;
edge[i ^ 1].flow -= Min;
cost += edge[i].cost*Min;
}
flow += Min;
}
return flow;
}
int main() {
init();
int n,m,p;
int cntdr=0;
scanf("%d%d%d",&n,&m,&p);
for(int i=0; i<n; i++) {
scanf("%s",mp[i]);
for(int j=0; j<m; j++) {
if(mp[i][j]=='1') {
door.push_back(make_pair(i+1,j+1));
cntdr++;
}
}
}
for(int i=0; i<p; i++) {
scanf("%d%d%d",&pep[i].x,&pep[i].y,&pep[i].pwr);
}
int s=0;//源点
int t=p+1+(n+m+p)*cntdr+1;//汇点
//0:汇点 1~p:人 p+1~p+(n+m+p):第一个门分出的每个时刻的点 p+(n+m+p)*(cntdoor-1)~p+(n+m+p)*cntdoor:最后一个门分出的每个时刻的点
for(int i=0; i<door.size(); i++) {
for(int j=1; j<=n+m+p; j++) {//一个人最多要n+m+p秒出去
int tmp=p+i*(n+m+p)+j;//表示第i个门在第j秒的状态
addedge(tmp,t,1,0);
if (j!=n+m+p)//只要不是这个门的最后一个
addedge(tmp,tmp+1,inf,1);//就要向这个门的下一秒连边
}
}
for(int i=0; i<p; i++) {
addedge(0,1+i,1,0);
for(int j=0; j<door.size(); j++) {
int dist=caldist(pep[i].x,pep[i].y,door[j].first,door[j].second);
addedge(1+i,p+j*(n+m+p)+dist,1,dist);//每个人向每个他刚好到门的时间的点连边
//这样,只要他如果被堵住,就会走到这个门的下一秒,只不过费用增加了
}
}
int ans=0;
minCostMaxflow(s,t,ans);
printf("%d\n",ans);
return 0;
}