您的位置:首页 > 其它

HDU 4411 Arrest 费用流 建边技巧

2014-09-08 23:24 351 查看
题意:有(N+1)个城市,M条路,每条路有距离。

在城市0有警察总局,里面有K个警察。从城市1到城市N,每个城市都有盗贼。我们要派出警察消灭这些盗贼。

警察到达一个城市可以选择抓捕盗贼或者不抓捕盗贼。且最后,所有的警察必须回到警察总局。

当第i个城市的盗贼被逮捕时,他们回向第i-1个城市发送警报,如果有盗贼收到警报,那这个行动就失败了。

求:在完成任务的情况下,最小化所有警察走过的距离。

思路:对于盗贼的警报,我们要从第一个城市开始,按顺序抓捕。

首先我们可以通floyd算法求出多源最短路。

接下来我们可以用最小费用流建图:

1.从源点s向城市0连容量为k,费用为0的边,表示至多可以使用k个警察。

2.从城市0向其他城市i,连容量为INF,费用为d[0][i]的边,表示从总局直接向该城市派警察。

3.城市i向汇点t连容量为INF,费用为d[0][i],表示警察直接从该城市回到总局。

4.城市0向汇点t连容量为K,费用为0的边,表示至多有K个警察可以不用出动,一直待在总局。

5.因为对于每个城市,我们必须要处理,且只能处理一次。在网络流中,对于至多访问一次的点,我们用拆点进行处理。而必须要处理的点,可以设其费用为-INF,由于是求最小费用,那该点一定会被处理到(否则就不是最小费用了)。所以,我们将城市i拆成入点:点i,出点:点i+N.从入点到出点连费用为-INF,容量为1的边。同时,2中的边要连向入点,3中的边要从出点连出。

6.这个题最难处理的其实是顺序访问。我们要对城市i的出点i+N,向位于他后面的城市j(i < j <= N)的入点连容量为INF,费用为的d[i][j]的边。这样我们可以保证是顺序访问了。

这样我们就可以跑出最小费用,然后加上N * INF,就是最终的答案。

几点小小的解释:

1.对于建边中2,3,6,条,边的容量可以为1。这是因为,如果多个警察派往一个城市,处理的盗贼不变,但总的距离多了,所以相比,最好的方案是让每个警察去抓不同的盗贼,完成不同的任务。

2.对于警察到达一个城市可以选择抓捕盗贼还是不抓的要求,是通过floyd算法求最短路实现的。因为最短路相当于从起点出发,在路上隐身,到终点抓盗贼。

3.对6的建边方法能够保证顺序访问:首先,如果多个警察出动,那我们可以单独安排每个警察的行动,从而保证抓捕顺序的先后。而对于一个警察,因为总是从排在前面的城市向排在后面的城市连边,也能保证抓捕顺序的先后。在建图时,我们对城市i,向他后面的所有城市连边,也保证了多个警察行动,对一个警察而言,会出现跳跃式的抓捕(因为其他的警察已经抓了)的情况。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;

struct edge{
int from,to,cap,flow,cost;
edge(int u = 0,int v = 0, int c= 0,int f = 0, int w = 0):
from(u),to(v),cap(c),flow(f),cost(w){}
};
struct mcmf{
int n,m,s,t;
static const int MAX = 20000;
static const int INF = 0x3f3f3f3f;
edge edges[MAX];
int head[MAX];
int next[MAX];
int tot;
int que[MAX],front,tail;
bool inq[MAX];
int d[MAX];
int p[MAX];
int a[MAX];
void init(){
memset(head,-1,sizeof(head));
tot = 0;
}
void addedge(int from, int to, int cap, int cost){
edges[tot] = edge(from,to,cap,0,cost);
next[tot] = head[from],head[from] = tot++;
edges[tot] = edge(to,from,0,0,-cost);
next[tot] = head[to],head[to] = tot++;
}
bool bellmanford(int s, int t,int & flow, int &cost){
memset(d,0x3f,sizeof(d));
memset(inq,false,sizeof(inq));
d[s] = 0,inq[s] = true,p[s] = 0,a[s] = INF;
front = tail = 0;
que[tail++] = s;
while(front < tail){
int u = que[front++];inq[u] = false;
for(int v = head[u]; v != -1; v = next[v]){
edge & e = edges[v];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost){
d[e.to] = d[u] + e.cost;
p[e.to] = v;
a[e.to] = min(a[u],e.cap - e.flow);
if(!inq[e.to])
que[tail++] = e.to,inq[e.to] = true;
}
}
}
if(d[t] == INF) return false;
flow += a[t]; cost += d[t];
for(int u = t; u != s; u = edges[p[u]].from){
//printf("%d ",u);;
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -=a[t];
}
// printf("\n");
return true;
}
int mincost(int s,int t){
int flow = 0,cost = 0;
while(bellmanford(s,t,flow,cost));
return cost;
}
} solver;

const int MAX = 200;
const int INF = 0x3f3f3f;

int N,M,K;
int d[MAX][MAX];
int x,y,t;

void floyd()
{
for(int i = 0 ; i < N; ++i)
d[i][i] = 0;
for(int k = 0 ; k <= N;++k)
for(int i = 0 ; i <= N; ++i)
for(int j = 0 ; j <= N; ++j)
d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}

int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%d %d %d", &N,&M,&K),N||M||K){

memset(d,0x3f,sizeof(d));
for(int i = 0; i < M; ++i){
scanf("%d %d %d", &x, &y, &t);
d[x][y] = d[y][x] = min(t,d[x][y]);
}
floyd();
int s = 2 * N + 1, t  = 2 * N + 2;
solver.init();
solver.addedge(s,0,K,0);
solver.addedge(0,t,K,0);
for(int i = 1; i <= N; ++i){
solver.addedge(0,i,1,d[0][i]);
solver.addedge(i+N,t,1,d[0][i]);
solver.addedge(i,i+N,1,-INF);
for(int j = i + 1; j <= N; ++j)
solver.addedge(i + N,j,1,d[i][j]);
}
printf("%d\n",solver.mincost(s,t) + N * INF);
}
return 0;
}




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: