poj 2195 Going Home

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;

}

 

你可能感兴趣的:(home)