链接:http://poj.org/problem?id=1087
题意:一个房间里,有N种插座,每种一个。现在有M种电器,每种一个。又有K种适配器(a,b),能将a类型插座,转换为b类型的插座。就是说,适配器自己通过插座b插到房间内的插座上,而对电器提供a型插座。适配器的个数无上限。
求最少没有相应插座可插的电器的数目。
建图。
参考了网上的思路。
汇点和插座连边,边权为1;
源点与电器连边,边权为1;
电器与相应的插座连边,边权为1;
适配器在插座类型a和b之间连边,边权为INF。
建好图,再用网络流的算法的解题。
至于如何构图,还是在慢慢摸索中啊。为什么它连它,它又连它,有点搞不清楚。
1.做这个题目,倒是学习了容器map的使用方法。
2.还有一点,源点和汇点不一定是标号最小的点和标号最大的点,对EK和Dinic都是如此。
我写了EK和Dinic两个版本。
//Dinic
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#define INF 0x7fffffff
#define MAX 600
#define MIN(a,b) a>b?b:a
using namespace std;
int n,m,k,ans,num;
int st,ed;
int r[MAX][MAX];
int vist[MAX],pre[MAX];
int dis[MAX];
map<string,int> plug;
//学习使用了容器map
//要加头文件#include<iostream>
//加头文件#include<string>
//count方法的使用
int BFS()// 重新 建 图 (按 层数 建图)
{
memset(dis,-1,sizeof(dis));
dis[st] = 0 ;
queue<int> q;
q.push(st);
while(!q.empty())
{
int k = q.front();
q.pop() ;
for( int i = 0;i<= num;i++)
{
if(r[k][i] > 0 && dis[i] < 0 )// 如果可以到达,但还没有访问
{
dis[i] = dis[k]+ 1 ;
q.push(i) ;
}
}
}
if(dis[ed] > 0) return 1;
else return 0 ;
}
int DFS(int x,int low)// 查找路径上的最小的流量
{
int i , a ;
if(x == ed) return low ;
for(i = 0;i<=num;i++)
{
if(r[x][i] > 0 && dis[i] == dis[x] + 1 && (a =DFS(i,min(low,r[x][i]))))
{
r[x][i] -= a;
r[i][x] += a;
return a ;
}
}
return 0 ;
}
void Dinic()
{
int res=0;
while(BFS())
{
while((res=DFS(st,INF)))
ans+=res;
}
return;
}
int main()
{
int i;
char a[26],b[26];
memset(r,0,sizeof(r));
st=0; //源点
ed=1; //汇点,用Dinic做,实践证明汇点不一定是标号最大的那个点
num=2; //所有顶点个数
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",a);
plug[a]=num++;
r[plug[a]][ed]=1; //插座与汇点相连,边权为1
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%s%s",a,b);
plug[a]=num++;
r[st][plug[a]]=1; //电器与源点相连,边权为1
if(plug.count(b)==0) //如果没有插座b类型,则添加plug[b],num++,下同
plug[b]=num++;
r[plug[a]][plug[b]]=1; //电器连向插座,边权为1
}
scanf("%d",&k);
for(i=1;i<=k;i++)
{
scanf("%s%s",a,b);
if(plug.count(a)==0)
plug[a]=num++;
if(plug.count(b)==0)
plug[b]=num++;
r[plug[a]][plug[b]]=INF; //插座a能转换成插座b,a连向b
}
ans=0;
Dinic();
printf("%d\n",m-ans);
return 0;
}
//EK
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#define INF 0x7fffffff
#define MAX 600
#define MIN(a,b) a>b?b:a
using namespace std;
int n,m,k,ans,num;
int st,ed;
int r[MAX][MAX];
int vist[MAX],pre[MAX];
map<string,int> plug;
//学习使用了容器map
//要加头文件#include<iostream>
//加头文件#include<string>
//count方法的使用
int BFS(int st,int ed)
{
int p,i;
queue<int> q;
memset(pre,-1,sizeof(pre));
memset(vist,0,sizeof(vist));
pre[st]=st;
vist[st]=1;
q.push(st);
while(!q.empty())
{
p=q.front();
q.pop();
for(i=0;i<=num;i++)
{
if(r[p][i]>0&&!vist[i])
{
pre[i]=p;
vist[i]=1;
if(i==ed)
return 1;
q.push(i);
}
}
}
return 0;
}
void EK(int st,int ed)
{
int d,i;
while(BFS(st,ed))
{
d=INF;
for(i=ed;i!=st;i=pre[i])
d=d<r[pre[i]][i]?d:r[pre[i]][i];
for(i=ed;i!=st;i=pre[i])
{
r[pre[i]][i]-=d;
r[i][pre[i]]+=d;
}
ans+=d;
}
return ;
}
int main()
{
int i;
char a[26],b[26];
memset(r,0,sizeof(r));
st=0; //源点
ed=1; //汇点
num=2; //所有顶点个数
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",a);
plug[a]=num++;
r[plug[a]][ed]=1; //插座与汇点相连,边权为1
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%s%s",a,b);
plug[a]=num++;
r[st][plug[a]]=1; //电器与源点相连,边权为1
if(plug.count(b)==0) //如果没有插座b类型,则添加plug[b],num++,下同
plug[b]=num++;
r[plug[a]][plug[b]]=1; //电器连向插座,边权为1
}
scanf("%d",&k);
for(i=1;i<=k;i++)
{
scanf("%s%s",a,b);
if(plug.count(a)==0)
plug[a]=num++;
if(plug.count(b)==0)
plug[b]=num++;
r[plug[a]][plug[b]]=INF; //插座a能转换成插座b,a连向b
}
ans=0;
EK(st,ed);
printf("%d\n",m-ans);
return 0;
}