鏈接: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;
}