poj 2195&&hdu 1533 最小费用流

题目链接:http://poj.org/problem?id=2195

题目大意:给你一个矩形里面有相同数目的人和房子,每个人只能进一个房子,问题求是所有的人进入房子的最小距离和,相邻之间为一步。

本题可以用最小费用最大流来解,之前对这一类题一直用所畏惧,之前学过一段时间没学会,放弃了,正好利用这个暑假,把网络流攻破。

自己看了王晓东那本书上关于最小费用路算法的讲解,但没写过,不知道怎么实现,就找了位大牛的参考。好了不废话了,下面说此题。

我们可以这样构图,让每个人与每个房间相连,边的容量为1,费用为它们之间的距离,同时构造一个超级源点和超级汇点,源点与每个人相连费用为0容量为1(这样

可以保证每个人只进一个房间,同时每个房间与汇点相连,费用为0,容量为1;这样求得的最小费用就是所求的最小距离和。

  1 #include<cstdio>
2 #include<iostream>
3 #include<cstring>
4 using namespace std;
5 #define size 500
6 #define max 10000
7 #define inf 0x7ffffff
8 int cap[size][size]; //存储各边容量
9 int cost[size][size]; //存储各边花费
10 int que[max],pre[size],dis[size]; //que队列数组(这里巧妙的一点是使用了循环队列)pre数组记录前驱结点 dis数组记录最小距离(花费)
11 bool vis[size]; //标记各点是否进队列
12 char map[size][size]; //此题没用 可以存原始矩形
13 int s,t; //s源点t终点
14 int r,c;
15 int ans; //最小费用流(此题也就是最短距离)
16 //求较小数
17 int minc(int a,int b)
18 {
19 if(a<b)
20 return a;
21 return b;
22 }
23
24 //取绝对值
25 int fabs1(int x)
26 {
27 if(x>0)return x;
28 else return -x;
29 }
30 //记录各点的最原始位置(坐标)
31 struct node
32 {
33 int x,y;
34 }m[size],h[size];
35
36 //求最短路(也叫最小费用路)
37 bool spfa(int s,int t)
38 {
39 int head=0,tail=1;
40 for(int i=0;i<=t;i++)
41 dis[i]=inf;
42 memset(vis,0,sizeof(vis));
43 que[head]=s;
44 dis[s]=0;
45 vis[s]=true;
46 while(head!=tail)
47 {
48 int tmp=que[head++],j;
49 for(j=0;j<=t;j++)
50 {
51 if(cap[tmp][j]&&dis[j]>dis[tmp]+cost[tmp][j])
52 {
53 dis[j]=dis[tmp]+cost[tmp][j];
54 pre[j]=tmp;
55 if(!vis[j])
56 {
57 que[tail++]=j;
58 if(tail==max)
59 tail=0;
60 }
61 }
62 }
63
64 vis[tmp]=false;
65 }
66
67 if(dis[t]!=inf)
68 return true;
69 return false;
70
71 }
72
73 //求距离
74 int len(int i,int j)
75 {
76 int a=fabs1(m[i].x-h[j].x);
77 int b=fabs1(m[i].y-h[j].y);
78 return a+b;
79 }
80
81 // 沿最小费用路增流
82 void end()
83 {
84 int mini=inf,i;
85 for(i=t;i!=s;i=pre[i])
86 {
87 mini=minc(mini,cap[pre[i]][i]);
88 }
89
90 for(i=t;i!=s;i=pre[i])
91 {
92 cap[pre[i]][i]-=mini;
93 cap[i][pre[i]]+=mini;
94 ans+=cost[pre[i]][i]*mini;
95 }
96 }
97
98 int main()
99 {
100 char ch;
101 while(scanf("%d%d",&r,&c)!=EOF)
102 {
103 ans=0;
104 if(r==0&&c==0)
105 break;
106 memset(cap,0,sizeof(cap));
107 memset(cost,0,sizeof(cost));
108 int posm=1,posh=1,i,j;
109 for(i=1;i<=r;i++)
110 for(j=1;j<=c;j++)
111 {
112 cin>>ch;
113 if(ch=='m')
114 {
115 m[posm].x=i;
116 m[posm++].y=j;
117 }
118 else if(ch=='H')
119 {
120 h[posh].x=i;
121 h[posh++].y=j;
122 }
123 }
124 s=0; //构造超级源点
125 posm--;
126 posh--;
127 t=posm*2+1; //超级汇点
128 for(i=1;i<=posm;i++) //构造源点到各点的边
129 {
130 cap[s][i]=1;
131 cost[s][i]=0;
132 }
133 for(i=posm+1;i<t;i++) //构造各点到汇点的边
134 {
135 cap[i][t]=1;
136 cost[i][t]=0;
137 }
138
139 for(i=1;i<=posm;i++) //构造各点之间的边
140 for(int j=1;j<=posh;j++)
141 {
142 cap[i][j+posm]=1;
143 cost[i][j+posm]=len(i,j);
144 cost[j+posm][i]=-cost[i][j+posm];
145 }
146
147 while(spfa(s,t))
148 end();
149 printf("%d\n",ans);
150 }
151 return 0;
152 }
posted @ 2011-08-16 15:34  我们一直在努力  阅读(404)  评论(0编辑  收藏  举报