zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    事件记录
    C++和extern C
    中断控制器
    NAND FLASH控制器
    MMU实验
    存储管理器实验
    GPIO实验
    linux与Windows使用编译区别及makefile文件编写
    ubuntu如何为获得root权限
    VI常用命令及linux下软件
  • 原文地址:https://www.cnblogs.com/liulangye/p/2516979.html
Copyright © 2011-2022 走看看