POJ-2195 最小费用最大流模板题

     曾经看网络流~搞了最大流~搞了最小割..搞到最小费用最大流就卡住了..因为那时候不会SPFA..刚才拿过来看...感觉理解如何来求了..试着自己写了一个去A..就过了这道模板题..

     POJ2195的构图很简单了...其实最好的解法应该是KM..去年暑假就试着用KM写过这题..结果WA得一直不明不白..今天用网络流1A了...

    关于最小费用最大流的课件还是很多的...我目前的理解就是每次找起点到终点的最短路径做为增广路经来更新..直道找不到最短路径...而找最短路径就需要边的权值..初始时每条边的权值为该边单位流量费用..而在找到一条路径做剩余网络时..这条路径每条边做反向边,权值为正向单位流量费用的相反数...

     至于为什么要用SPFA而不能用Djikstra..是因为在做剩余网络时会出现负边...做SPFA..只有当边还有剩余的容量时才能更新...


Program: 

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<queue>
#define oo 2000000000
using namespace std;
struct node1
{
      int y,x;
}man[105],house[105];
struct node2
{
      int f,w;
}s[205][205];
struct node3
{
      int pre,w;
}dp[205];
int n,m,NumMan,NumHouse,ans;
char arc[105][105];
bool used[205],inqueue[205];
queue<int> myqueue;
bool SPFA()
{
      int h,k,i;
      while (!myqueue.empty()) myqueue.pop();
      memset(dp,-1,sizeof(dp));
      memset(inqueue,false,sizeof(inqueue));
      myqueue.push(0);  dp[0].w=0;      
      while (!myqueue.empty())
      {
             h=myqueue.front();
             myqueue.pop();
             inqueue[h]=false;
             for (i=0;i<=n;i++)
             if (s[h][i].f && (dp[i].pre==-1 || dp[i].w>dp[h].w+s[h][i].w))
             {
                     dp[i].w=dp[h].w+s[h][i].w;
                     dp[i].pre=h;
                     if (!inqueue[i])
                     {
                            inqueue[i]=true;
                            myqueue.push(i);
                     }
             }
      }
      if (dp[n].pre==-1) return false;
      return true;
}
void MinCostOfMaxFlow()
{
      int i,m,way[205],Flow;
      ans=0;
      while (SPFA())
      {
            m=0;
            i=n;
            memset(way,0,sizeof(way));
            while (1)
            {
                  way[++m]=i;
                  if (!i) break;
                  i=dp[i].pre;
            }
            Flow=oo;
            for (i=1;i<m;i++) 
               if (s[way[i+1]][way[i]].f<Flow) Flow=s[way[i+1]][way[i]].f;
            for (i=1;i<m;i++)
            {
                  ans+=s[way[i+1]][way[i]].w*Flow;
                  s[way[i+1]][way[i]].f-=Flow;
                  s[way[i]][way[i+1]].f+=Flow;
                  s[way[i]][way[i+1]].w=-s[way[i+1]][way[i]].w;
            } 
      }
}
int absI(int x)
{
      if (x<0) return -x;
      return x;
}
int main()
{
      freopen("input.txt","r",stdin);
      freopen("output.txt","w",stdout);
      int i,j;
      while (~scanf("%d%d\n",&n,&m))
      {
            if (!n && !m) break;
            for (i=1;i<=n;i++) gets(arc[i]+1);
            NumMan=NumHouse=0;
            for (i=1;i<=n;i++)
               for (j=1;j<=m;j++)
                  if (arc[i][j]=='m')
                  {
                         NumMan++;
                         man[NumMan].y=i;
                         man[NumMan].x=j;                                     
                  }else
                  if (arc[i][j]=='H')
                  {
                         NumHouse++;
                         house[NumHouse].y=i;
                         house[NumHouse].x=j;
                  } 
            memset(s,0,sizeof(s));
            for (i=1;i<=NumMan;i++)
               for (j=1;j<=NumHouse;j++)
               {
                      s[i][j+NumMan].f=1;
                      s[i][j+NumMan].w=absI(man[i].x-house[j].x)+absI(man[i].y-house[j].y); 
               }
            for (i=1;i<=NumMan;i++)
            {
                    s[0][i].f=1;
                    s[0][i].w=0; 
            }
            for (i=1;i<=NumHouse;i++)
            {
                    s[i+NumHouse][NumMan+NumHouse+1].f=1;
                    s[i+NumHouse][NumMan+NumHouse+1].w=0;
            }
            n=NumMan+NumHouse+1;  
            MinCostOfMaxFlow();
            printf("%d\n",ans);            
      }
      return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值