刚读的时候以为是Optimal Milking那道题,但实际上不是的;但借此我也想清了一些东西。
optimal milking里面问的是走的最远的那头奶牛最少走多少,对应到这道题里就是走的最远的people最少走多少;若是这样的话就得二分找可行流了,不然的话没有办法建图。(几乎一样的题稍微问了个不一样的东西,就从原本用最大流解变成用费用流解,让我觉得很奇妙)
这里面问的一共最少走多少,那就是费用流了。
#include<iostream> #include<deque> #include<vector> #include<cstring> #include<cmath> #define INF 2e9 using namespace std; int T,ans; struct edge{ int v,cap,reverse,cost; }; vector<int> edges[1005];//邻接表 vector<edge> bian;//所有的边都存在里面 vector< pair<int,int> > people,house; char maze[105][105]; void addedge(int u,int v,int cost,int cap){//u到v一条边,再加一条反向边 edge e; e.cap=cap; e.v=v; e.cost=cost; e.reverse=bian.size()+1;//这条边的反边将建在这条边之后(这条边建完后在bian.size()的位置) bian.push_back(e); edges[u].push_back(bian.size()-1); e.cap=0; e.v=u; e.cost=-cost; e.reverse=bian.size()-1; bian.push_back(e); edges[v].push_back(bian.size()-1); } int dist[1005],pre[1005];//pre[i]代表走的哪条边到的i点 bool spfa(){ for(int i=0;i<=T;i++) dist[i]=INF; for(int i=0;i<=T;i++) pre[i]=-1; deque<int> q; dist[0]=0; pre[0]=-1; q.push_back(0); while( !q.empty() ){ int u = q.front(); q.pop_front(); for(int i=0;i<edges[u].size();i++){//所有以u为起点的边的边的索引 edge &e = bian[ edges[u][i] ]; int v=e.v; if( e.cap>0 && dist[u]+e.cost<dist[v] ){ dist[v] = dist[u]+e.cost; pre[v]=edges[u][i]; q.push_back(v); } } } if(dist[T]==INF) return false; return true; } int EK(){ int max_flow=0; while( spfa() ){ //cout<<"suc"<<endl; int u=T,minflow=INF;//在终点位置 while( u!=0 ){ edge &e = bian[ pre[u] ]; minflow=min(minflow,e.cap); u = bian[ e.reverse ].v; } ans+=minflow*dist[T]; u=T; while( u!=0 ){ edge &e = bian[ pre[u] ]; e.cap-=minflow; bian[ e.reverse ].cap+=minflow; u = bian[ e.reverse ].v; } } return max_flow; } int main(){ int n,m,k; while(1){ cin>>n>>m; if(n==0 && m==0) break; bian.clear(); people.clear(); house.clear(); for(int i=0;i<=T;i++) edges[i].clear(); ans=0; //people:1-n house: n+1 - 2*n for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cin>>maze[i][j]; if( maze[i][j]=='H' ) house.push_back( make_pair(i,j) ); else if( maze[i][j]=='m' ) people.push_back( make_pair(i,j) ); } //people向house建边 费用流 for(int i=0;i<people.size();i++){ for(int j=0;j<house.size();j++){ int dis=0; dis = abs(people[i].first-house[j].first)+abs(people[i].second-house[j].second); addedge( i+1,people.size()+j+1,dis,1 ); } } T=2*people.size()+1; //源点到所有人 for(int i=0;i<people.size();i++) addedge(0,i+1,0,1); //house到汇点 for(int i=0;i<house.size();i++) addedge( people.size()+i+1,T,0,1 ); EK(); cout<<ans<<endl; } return 0; }