寒假Day14:最小费用最大流问题 POJ2195-Going Home
POJ2195-Going Home
Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point.
You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
InputThere are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.
OutputFor each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.
Sample Input
2 2 .m H. 5 5 HH..m ..... ..... ..... mm..H 7 8 ...H.... ...H.... ...H.... mmmHmmmm ...H.... ...H.... ...H.... 0 0
Sample Output
2 10 28
题意:
给出n、m,n行m列,‘ . ’代表空地,H代表房子,m代表man,有同样数目的人和房子(数量≤100),每个人对应一座房子;
问:最少需要花费多少美元(每个人需要走的步数)才能使得每个人都到房子里去(一人只能进入一所);
间接性问:最小费用最大流。
思路:
- 源点s到每个人建立一条容量为1,费用为0的边;
- 每个人到每个房子建立一条容量为1,费用为两者的距离;
- 每个房子到汇点t建立一条容量为1,费用为0的边;
- 计算走的步数:曼哈顿距离
- 也可以用二分图匹配(KM算法来做)
注意:
- 数组开的大小;
- 学会如何建边;
- 看清字母啊大哥,是H和m,看清大小写啊你个猪
难点还是在于建边:
s=0; //源点s t=hh+mm+1; //汇点 for(int i=1; i<mm; i++) add(s,i,1,0);//源点s到每个人建立一条容量为1,费用为0的边 for(int i=1; i<mm; i++) { for(int j=1; j<hh; j++) { int dd=d(H[i].x,H[i].y,M[j].x,M[j].y); add(i,j+hh,1,dd);//每个人到每个房子建立一条容量为1,费用为两者的距离边 } } for(int i=1; i<hh; i++) add(i+hh,t,1,0);//每个房子到汇点t建立一条容量为1,费用为0的边
AC代码:
1 #include<string.h> 2 #include<iostream> 3 #include<stdio.h> 4 #include<algorithm> 5 #include<queue> 6 #include<vector> 7 #include<map> 8 #include<cmath> 9 using namespace std; 10 #define inf 0x3f3f3f3f 11 const int N=1100; 12 typedef long long ll; 13 14 struct node 15 { 16 int to,nextt; 17 int cap,flow,cost; 18 int x,y; 19 } e[N*N],H[N*N],M[N*N]; 20 21 bool book[N*N]; 22 int pre[N*N],head[N*N],dist[N*N]; 23 int tot,s,t; 24 char a[N][N]; 25 26 int d(int x1,int y1,int x2,int y2) 27 { 28 return abs(x2-x1)+abs(y2-y1); 29 } 30 31 void add(int u,int v,int cap,int cost) 32 { 33 e[tot].to=v; 34 e[tot].cap=cap; 35 e[tot].cost=cost; 36 e[tot].flow=0; 37 e[tot].nextt=head[u]; 38 39 head[u]=tot++; 40 e[tot].to=u; 41 e[tot].cap=0; 42 e[tot].cost=-cost; 43 e[tot].flow=0; 44 e[tot].nextt=head[v]; 45 head[v]=tot++; 46 } 47 48 bool SPFA() 49 { 50 for(int i=0; i<=t; i++) 51 { 52 dist[i]=inf; 53 book[i]=0; 54 pre[i]=-1; 55 } 56 book[s]=1; 57 dist[s]=0; 58 queue<int>Q; 59 Q.push(s); 60 while(!Q.empty()) 61 { 62 int u=Q.front(); 63 Q.pop(); 64 book[u]=0; 65 for(int i=head[u]; i!=-1; i=e[i].nextt) 66 { 67 int v=e[i].to; 68 if(e[i].cap>e[i].flow&&dist[v]>dist[u]+e[i].cost) 69 { 70 dist[v]=dist[u]+e[i].cost; 71 pre[v]=i; 72 if(book[v]==0) 73 { 74 book[v]=1; 75 Q.push(v); 76 } 77 } 78 } 79 } 80 if(dist[t]!=inf) 81 return 1; 82 return 0; 83 } 84 85 int MCMF() 86 { 87 int flow=0,cost=0; 88 while(SPFA()) 89 { 90 int minn=inf; 91 for(int i=pre[t]; i!=-1; i=pre[e[i^1].to]) 92 minn=min(minn,e[i].cap-e[i].flow); 93 for(int i=pre[t]; i!=-1; i=pre[e[i^1].to]) 94 { 95 e[i].flow+=minn; 96 e[i^1].flow-=minn; 97 cost+=e[i].cost*minn; 98 } 99 flow+=minn; 100 } 101 return cost; 102 } 103 104 int main() 105 { 106 int n,m; 107 while(~scanf("%d %d",&n,&m)) 108 { 109 if(n==0&&m==0) 110 break; 111 memset(head,-1,sizeof(head)); 112 int hh=1,mm=1; 113 for(int i=0; i<n; i++) 114 { 115 scanf("%s",a[i]); 116 for(int j=0;j<m;j++) 117 { 118 if(a[i][j]=='H')//home 119 { 120 H[hh].x=i; 121 H[hh++].y=j; 122 123 } 124 else if(a[i][j]=='m')//man 125 { 126 M[mm].x=i; 127 M[mm++].y=j; 128 } 129 } 130 } 131 132 s=0; 133 t=hh+mm+1; 134 for(int i=1;i<mm;i++) 135 add(s,i,1,0);//源点s到每个人建立一条容量为1,费用为0的边 136 137 for(int i=1;i<mm;i++) 138 { 139 for(int j=1;j<hh;j++) 140 { 141 int dd=d(H[i].x,H[i].y,M[j].x,M[j].y); 142 add(i,j+hh,1,dd);//每个人到每个房子建立一条容量为1,费用为两者距离的边 143 } 144 } 145 146 for(int i=1;i<hh;i++) 147 add(i+hh,t,1,0);//每个房子到汇点t建立一条容量为1,费用为0的边 148 149 int mincost=MCMF(); 150 printf("%d\n",mincost); 151 } 152 return 0; 153 }
待解决:
- POJ2195数组大小问题:
这题的数组到底应该开多大啊???好不容易试过一组,可是不知道数组怎么确定大小啊???总不能每次都试叭叭叭
我觉得应该是这样子的,我的理由是从源点s到所有的人连N条边,人和房子连N*N条边,房子到汇点t连N条边,所有边加起来=12320,可是这样不对,
邻接表开4倍、10倍、100倍都RT了,
为什么???
//12320=N+N*N+N=110+110*110+110 const int N=12310;
struct node { int to,nextt; int cap,flow,cost; int x,y; } e[110*110],H[110],M[110];
bool book[N]; int pre[10*N],head[10*N],dist[N]; int tot,s,t; char a[110][110];
我是这样写才试过的(已AC)
const int N=1100; struct node { int to,nextt; int cap,flow,cost; int x,y; } e[N*N],H[N*N],M[N*N]; bool book[N*N]; int pre[N*N],head[N*N],dist[N*N]; int tot,s,t; char a[N][N];