@morehigh
2017-02-25T11:16:44.000000Z
字数 5089
阅读 1192
二分图
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大独立集 = 顶点数 - 最大匹配数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
A - COURSES
题意:
满足两个条件:
1.如果这个学生学习一门课程,可以作为这门课的代表
2.每门课程都有一个代表
输入t个案例,P (1 <= P <= 100)指有P门课程,N (1 <= N <= 300)指有N个学生,每门课程有若干个学生学习,若每门学生都有一个代表从输出“YES”,否则“NO”
解题思路:
让每门课与学习这门课的学生匹配,使每门课有各自不同的代表
匈牙利算法
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
int map[105][305];
int match[305];
int use[305];
int p,n;
bool find(int x)
{
for(int i=1;i<=n;i++)
{
if(!use[i]&&map[x][i])
{
use[i]=1;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return true;
}
}
}
return false;
}
int sol()
{
int ans=0;
for(int i=1;i<=p;i++)
{
memset(use,0,sizeof(use));
if(find(i))
ans++;
}
return ans;
}
int main()
{
int t,num,stu;
cin>>t;
while(t--)
{
memset(map,0,sizeof(map));
memset(match,-1,sizeof(match));
scanf("%d%d",&p,&n);
for(int i=1;i<=p;i++)
{
scanf("%d",&num);
for(int j=1;j<=num;j++)
{
scanf("%d",&stu);
map[i][stu]=1;
}
}
int ans=sol();
if(ans==p)
printf("YES\n");
else
printf("NO\n");
}
}
B - The Accomodation of Students
题意:
n(1<n<=200)个学生,m对学生相互认识,是这些同学分成两组,使组中同学相互不认识。,
解题思路:
先判断是否能将同学分成两组,是否为二分图,用染色法判断,将相互认识的同学连边,然后将连边的两个量标记为1,-1.若发现相邻一样,则表明出现环,则不是二分图。若是二分图,则匈牙利算法求最大匹配
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int n,m;
int use[500],match[500];
int d[500];
vector<int> mp[500];
int dfs(int x,int f)
{
d[x]=f;
for(int i=0;i<mp[x].size();i++)
{
if(d[x]==d[mp[x][i]])
return 0;
if(d[mp[x][i]]==0)
{
// d[mp[x][i]]=-f;
if(!dfs(mp[x][i],-f))
return 0;
}
}
return 1;
}
int find(int x)
{
for(int i=0;i<mp[x].size();i++)
{
int y=mp[x][i];
if(!use[y])
{
use[y]=1;
if(match[y]==-1||find(match[y]))
{
match[y]=x;
return 1;
}
}
}
return 0;
}
int solve()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(use,0,sizeof(use));
if(find(i))
ans++;
}
return ans;
}
int main()
{
int a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(match,-1,sizeof(match));
for(int i=1;i<=n;i++)
if(mp[i].size())
mp[i].clear();
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
mp[a].push_back(b);
mp[b].push_back(a);
}
int flag=1;
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
{
if(mp[i].size())
if(!d[i])
if(!dfs(i,1))
{
flag=0;
break;
}
}
if(!flag)
cout<<"No"<<endl;
else
{
cout<<solve()/2<<endl;
}
}
return 0;
}
C - Guardian of Decency
题意:
老师要带一些同学去春游,为了防止同学之间早恋,若满足4个条件之一,则2个同学将发生早恋。T ≤ 100组样例,N ≤ 500名学生,然后每个同学4个属性。
解题思路:
二分图的最小独立集=点集-最大匹配,将满足4个条件能成为恋人的两人连线,得到最大匹配。
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n;
int ff,mm;
int map[510][510],match[510],use[510];
struct Node
{
int h;
char sex[2];
char mus[105],sp[105];
}pup[510];
int find(int x)
{
for(int i=0;i<n;i++)
{
if(!use[i]&&map[x][i])
{
use[i]=1;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int solve()
{
int ans=0;
for(int i=0;i<n;i++)
{
memset(use,0,sizeof(use));
if(find(i))
ans++;
}
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d",&n);
int h;
// ff=0,mm=0;
// char sex[2];
// char mus[105],sp[105];
for(int i=0;i<n;i++)
{
scanf("%d%s%s%s",&pup[i].h,pup[i].sex,pup[i].mus,pup[i].sp);
// printf("%d%s%s%s",h,sex,mus,sp);
}
memset(match,-1,sizeof(match));
memset(map,0,sizeof(map));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(abs(pup[i].h-pup[j].h)<=40&&strcmp(pup[i].mus,pup[j].mus)==0&&strcmp(pup[i].sp,pup[j].sp)!=0&&strcmp(pup[i].sex,pup[j].sex)!=0)
map[i][j]=map[j][i]=1;
}
}
int ans=solve()/2;
printf("%d\n",n-ans);
}
return 0;
}
D - Machine Schedule
题意:
n, m (n, m < 100),2台机器,第一台有n中模式,第二台有m种模式,有k个任务每个任务只能限定每一台机器只有一种模式能够工作,若要重启一次需要花费1的时间。求最少重启几次。
解题思路:
二分图的最小点覆盖=最大匹配。
二分匹配时,一边是第一台机器n种模式,另一边是第二台机器的m种模式,转换一种模式时就要重启一次因此遍历第一台的每个模式求出最大匹配就是重启多少次
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n,m,k;
int map[110][110],match[110],use[110];
int find(int x)
{
for(int i=0;i<m;i++)
{
if(!use[i]&&map[x][i])
{
use[i]=1;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int solve()
{
int ans=0;
for(int i=0;i<n;i++)
{
memset(use,0,sizeof(use));
if(find(i))
ans++;
}
return ans;
}
int main()
{
int j,x,y;
while(scanf("%d",&n)!=EOF&&n)
{
scanf("%d%d",&m,&k);
memset(map,0,sizeof(map));
memset(match,-1,sizeof(match));
for(int i=0;i<k;i++)
{
scanf("%d%d%d",&j,&x,&y);
if(x>0&&y>0)
map[x][y]=1;
}
cout<<solve()<<endl;
}
return 0;
}
E - Cat VS Dog
题意:
P个小朋友去参观动物园,动物园中有N只猫和M只狗,每个小朋友都会有喜欢一只狗(或猫),讨厌一只猫(或狗),饲养员要带走一些猫或者狗,然而带走一个小朋友喜欢的动物,小朋友会不开心,为了使更多小朋友开心,问最多使多少小朋友开心
解题思路:
二分图最大点独立集 = 顶点数 - 最大匹配数,对同一只动物喜欢和不喜欢的小朋友进行匹配,说明这两个小朋友具有排斥关系,由于我们是将每个小朋友都匹配一次,所以应该小朋友数-匹配数/2.
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n,m,k;
int map[510][510],match[510],use[510];
char lk[510][10],dislk[510][10];
int find(int x)
{
for(int i=0;i<k;i++)
{
if(!use[i]&&map[x][i])
{
use[i]=1;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int solve()
{
int ans=0;
for(int i=0;i<k;i++)
{
memset(use,0,sizeof(use));
if(find(i))
ans++;
}
// cout<<ans<<endl;
return ans;
}
int main()
{
int j,x,y;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(map,0,sizeof(map));
memset(match,-1,sizeof(match));
for(int i=0;i<k;i++)
{
scanf("%s%s",lk[i],dislk[i]);
}
for(int i=0;i<k;i++)
{
for(int j=0;j<k;j++)
{
if(strcmp(lk[i],dislk[j])==0||strcmp(lk[j],dislk[i])==0)
map[i][j]=map[j][i]=1;
}
}
cout<<k-solve()/2<<endl;
}
return 0;
}