首页 > 代码库 > maxflow1087E-K

maxflow1087E-K

poj 1087 (最大流或二分匹配)

题意其实容易读懂,就是:

1、在一个会议室里有n种插座,每种插座一个;

2、每个插座只能插一种以及一个电器(或者适配器);

3、有m个电器,每个电器有一个插头需要插在相应一种插座上;

4、不是所有电器都能在会议室找到相应插座;

5、有k种适配器,每种适配器可以有无限多数量;

6、每种适配器(a, b)可以把b类插座变为a类插座;

7、问最后有多少个电器无法使用。

 

本题可以用最大流或者二分图做。

下面讲解最大流: 

按照电流流向构图

1、各个电器各自为一个节点,和源点(不存在,自己构造)相连,且源点到电器的容量为1

2、将室内已有的插座各自为一个节点,和汇点(不存在,自己构造)相连,且到汇点的容量为1

3、将电器到其应该插的插座类型的点做边,且容量为1

4、将适配器(a, b)a点到b点做边,且边的容量为INF

需要注意,在输入电器以及输入适配器的过程中,如果遇到室内没有的插座类型,则需要在图中添加新的节点。

 

 

 

#include <iostream> 

#include <queue>

#include <map>

#include<string>

using namespace std;

const int INF = 0x7fffffff;

const int MAX = 600;

int r[MAX][MAX] , pre[MAX] , d[MAX];

map < string ,int > hash;

queue < int > Q;

int karp(int s,int t,int num)

{

     

     int Maxflow=0;

     while (1)

     {

           memset(pre,-1,sizeof(pre));     

           memset(d,0,sizeof(d));//找到一条增广路径后,重新把最小残余值(指的是顶点的,不是边)初始为0

           while (!Q.empty())Q.pop();

           Q.push(s);

           d[s]=INF;//d[i]表示从源点到i点经过的路径最小残留量,目的在于获取增广路径的最小残留量,即d[t]的残留量,

            while (!Q.empty())

            {

                  int q=Q.front(); Q.pop();     

                  for (int i=1;i<=num;i++)

                  {

                      if (d[i]==0&&r[q][i]>0)//d[i]=0表示没有被访问过,r>0表示存在边。r初始为0

                      {

                         pre[i]=q;                        

                         d[i]=d[q] > r[q][i] ? r[q][i] : d[q] ;  //取两者更小的

                         Q.push(i);

                      }

                  }

            }

            if (d[t]==0)break;//搜索不到t说明不存在增广路径,跳出while执行return Maxflow;

            for (int i=t;i!=s;i=pre[i])      

            {

                r[pre[i]][i]-=d[t];//d[t]是增广路径上最小的残留量

                r[i][pre[i]]+=d[t];   //反方向的也要考虑

            }

            Maxflow+=d[t];

     }

     return Maxflow;

}

int main()

{

    int l,m,n;/* l:室内插座数量; m:电器的数量;n:适配器种类数;*/

    char tmp1[30],tmp2[30];

    int s=1,t=2;//一号节点为源点,二号为汇点

    scanf("%d",&l);

    int num=3; //从3开始

    memset(r,0,sizeof(r));

    for (int i=1;i<=l;i++)

    {

         scanf("%s",tmp1);  //插座盒、电器均有名称。 

         hash[tmp1]=num;//NUM个节点对应名字为temp1的插座

//map容器将string类型的电器映射到数字,即将其编号,同时以方便后面查重

         r[num++][t]=1;//权值为1,表示插座最多只能有一个电器经过,且最终流入汇点

    }

    scanf("%d",&m);

    for (int i=1;i<=m;i++)

    {

        scanf("%s%s", tmp1 , tmp2 );    //temp1电器,temp2插座

        hash[tmp1]=num;

        r[s][num++]=1;//电器与源点s相连,最多只能有一个电器被使用

        if (hash.count(tmp2)==0)//如果出现之前没有出现的插座,表示该电器应该用该插座,但目前没有,不过后面用适配器可以转换

        {

            hash[tmp2]=num++;                       

        }

        r[hash[tmp1]][hash[tmp2]]=1;//电器与插座相连

    }

    scanf("%d",&n);

    for (int i=1;i<=n;i++)

    {

        scanf("%s%s",tmp1,tmp2);    

        if (hash.count(tmp1)==0)//也可能出现之前没出现过的

        {

            hash[tmp1]=num++;                       

        }

        if (hash.count(tmp2)==0)

        {

            hash[tmp2]=num++;                       

        }

        r[hash[tmp1]][hash[tmp2]]=INF;//适配器无数个,所以残留量无限大。

    }

    printf("%d\n",m-karp(s,t,num));

    //system("pause");

    return 0;    

}

maxflow1087E-K