http://poj.org/problem?id=2195
任何问题 都是 难了不会 会了不难 难就难在由不会变成会
尤其是刚接触到一个新知识点的时候硬着头皮,耐心地去看去理解,一定能学会,然后你就会发现它
原来并不难
本题是一个最小费用流
详解见代码注释
#include<iostream> #include<string> #include<cstring> #include<algorithm> #include<queue> #include<cstdio> using namespace std; const int N=1001;//后台数据水了 开1000就过了 其实更小也过不过也不能太小 其实理论上是10000 const int M=100000005; int flow[N][N];//保存流 int pay[N][N];//保存费用 struct node { int x,y; char c; }mem[10010];//保存输入的m点和H点 void find_flow_pay(int i,int j) { if(mem[i].c==mem[j].c)//两个点必须一个是m一个是H return ; if(mem[i].c=='m') { pay[i][j]=abs(mem[j].x-mem[i].x)+abs(mem[j].y-mem[i].y);//求费用 pay[j][i]=-pay[i][j];//反向费用变负的 flow[i][j]=1;//正向流为1 反向为0 } else//同上 只不过这个 j点为m而已 { pay[j][i]=abs(mem[j].x-mem[i].x)+abs(mem[j].y-mem[i].y); pay[i][j]=-pay[j][i]; flow[j][i]=1; } } int Spfa(int n) { bool in[N];//是否在队列中 int dist[N];//距离 int f[N];//前驱点 memset(in,false,sizeof(in)); for(int i=1;i<=n;++i) dist[i]=M;//初始化最大 dist[0]=0;//超级源点为0 queue<int>str; str.push(0); in[0]=true; while(!str.empty())//由于没有负环 所以直接找到队列为空就开 { int x=str.front(); str.pop(); in[x]=false; for(int i=1;i<=n;++i) { if(flow[x][i]>0&&dist[x ]+pay[x][i]<dist[i]) { dist[i]=dist[x]+pay[x][i];//更新距离 其实就是费用 f[i]=x;//标记前驱 if(in[i]==false)//当i不在队列中就进队列 并标记 { in[i]=true; str.push(i); } } } } if(dist[n]==M)//到达不了超级终端则返回0 return 0; int k=n; while(k!=0)//更新流 { int pre=f[k]; --flow[pre][k]; ++flow[k][pre]; k=pre; } return dist[n]; } int main() { int n,m; while(cin>>n>>m) { if(n==0&&m==0) break; int I=1; char ctemp; memset(flow,0,sizeof(flow)); memset(pay,0,sizeof(pay)); for(int i=1;i<=n;++i) { getchar();//吃掉回车 for(int j=1;j<=m;++j) { scanf("%c",&ctemp); if(ctemp!='.') { mem[I].c=ctemp;mem[I].x=i;mem[I].y=j; for(int l=1;l<I;++l) { find_flow_pay(l,I);//球两点的流和花费 } ++I; } } } for(int i=1;i<I;++i)//0 为超级源点 I 为超级终点 { if(mem[i].c=='m') { flow[0][i]=1; } else { flow[i][I]=1; } } int ans=0; while(1) { int k=Spfa(I); if(k==0)//无流更新 break; ans=ans+k; } cout<<ans<<endl; } return 0; }