任意两条边都没有公共端点的边的集合被称为图的一组匹配。在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配。
匈牙利算法(增广路算法)时间复杂度 O(NM)
能用二分图匹配的模型一般包括两个要素
0要素 每个集合内部有0条边
1要素 每个节点只能和一条匹配边相连
原题链接
题目大意
有一个N*M的棋盘 某些格子禁止放置 求最多能向棋盘放入多少个长为2 宽为1的骨牌
题目思路
把棋盘变成二分图,把所有的格子根据行与列的和的奇数偶数不同染成黑白两色 黑白两色互相连接对应一个骨牌占两个格子
首先确定两个要素
1要素每个格子只能有一个骨牌放置 ,0要素同色格子之间无法由骨牌连接 然后对于每一个黑色格子判断能否与身边的四个相连 如果超过界限或者禁止放置就不连接,否则连接
最后对于每个黑色的块判断有没有匹配边 如果有ans++,因为如果有n个骨牌 那么一定会在n个黑(白)色块里找到匹配边
#include
using namespace std;
const int maxn=1e2+10;
int n,t;
int mp[maxn][maxn];
struct node{
int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
int turn[4][2]={1,0,0,1,-1,0,0,-1};
void add(int u,int v,int d)
{
e[++cnt].dis=d;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
bool dfs(int x)
{
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(vis[y]==0)
{
vis[y]=1;
if(!match[y]||dfs(match[y]))
{
match[y]=x;
return 1;
}
}
}
return 0;
}
int main()
{
cin>>n>>t;
for(int i=1;i<=t;i++)
{
int a,b;
cin>>a>>b;
mp[a][b]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(mp[i][j]||(i+j)&1)continue;
for(int k=0;k<=3;k++)
{
int ii=i+turn[k][0];
int jj=j+turn[k][1];
if(mp[ii][jj]||ii<=0||ii>n||jj<=0||jj>n)continue;
add((i-1)*n+j,(ii-1)*n+jj,1);
//add((ii-1)*n+jj,(i-1)*n+j,1);
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((i+j)&1)continue;
if(mp[i][j])continue;
memset(vis,0,sizeof(vis));
if(dfs((i-1)*n+j))ans++;
}
}
cout<
题目链接
题目大意
与372差不多 但改成了 每个车之间不能相互攻击到
题目思路
题目转化为再每个行和每个列只能放一个车 但是有的地方不能放置 问你最多能放多少个车
将车的行转化为左点集 列转化为右点集合 如果某个点(i,j)不能放置意味着该点所在的行与列不能进行连边,即ai与bj不能连接 否则把该点所在的行与列进行连边 即ai与bj能连
#include
using namespace std;
const int maxn=200+10;
int n,m,t;
int mp[maxn][maxn];
struct node{
int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
int turn[4][2]={1,0,0,1,-1,0,0,-1};
void add(int u,int v,int d)
{
e[++cnt].dis=d;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
bool dfs(int x)
{
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(vis[y]==0)
{
vis[y]=1;
if(!match[y]||dfs(match[y]))
{
match[y]=x;
return 1;
}
}
}
return 0;
}
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=t;i++)
{
int a,b;
cin>>a>>b;
mp[a][b]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j])continue;
add(i,j,1);
//add(j+n,i)
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
//if(mp[i][j])continue;
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
cout<
题目链接
题目大意
有n个防御塔和m个敌人 每个防御塔可以进行无限次攻击 但是 每个防御塔进行攻击需要t1的准备时间 攻击完之后有t2冷却时间 导弹飞到怪兽地点需要两点的距离dis/速度v 问你如何在最短时间消灭所有怪兽 以上的条件T1 T2 v dis n m 防御塔和敌人的坐标都会给出
题目思路
遇到这种求最短时间之类的问题就很容易想到二分答案
由于每个导弹塔可以发送多发子弹 最多发m发 因此可以判断出这个题其实是一个多重匹配问题
因此我们需要进行拆点 将怪兽作为二部图的左边节点 将导弹塔所可能发出的每发导弹作为右部节点 预处理每个右部节点 预处理出来他们的初始发射时间 和所属于的导弹塔
然后对于check函数 如果这颗导弹的初始发射时间加上路程走的时间要小于等于mid的话就加上这条边 否则不加 然后对于每个左点进行dfs 最后如果ans==m则说明每个怪兽都会被消灭 return1 else return 0
代码
#include
using namespace std;
const int maxn=60;
double eps=1e-7;
int n,m,t;
double t1,t2;
int v,tot;
int mp[maxn][maxn*maxn];
struct node
{
int x,y;
}a[maxn],b[maxn];
struct new_node{
int id;double t;
}c[maxn*maxn];
struct xy{
int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
void add(int u,int v,int d)
{
e[++cnt].dis=d;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
bool dfs(int x)
{
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(vis[y]==0)
{
vis[y]=1;
if(!match[y]||dfs(match[y]))
{
match[y]=x;
return 1;
}
}
}
return 0;
}
double dis(const node &x,const node &y){
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
int check(double mid)
{
memset(match,0,sizeof(match));
memset(e,0,sizeof(e));
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=tot;j++)
{
if(c[j].t+dis(b[c[j].id],a[i])/v<=mid)
{
add(i,j,1);
}
}
}
int ans=0;
for(int i=1;i<=m;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
if(ans==m)return 1;
return 0;
}
int main()
{
cin>>n>>m>>t1>>t2>>v;
tot=n*m;
t1/=60;
for(int i=1; i<=m; i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1; i<=n; i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int k=(j-1)*n+i;
c[k].id=i;
c[k].t=(j-1)*(t1+t2)+t1;
}
}
// go(i,1,m)
// go(j,1,n){
// k=(i-1)*n+j;
// c[k].id=j;
// c[k].t=(i-1)*(t1+t2)+t1;
// }
double l=t1,r=(m+1)*(t1+t2)+10000;
while(r-l>eps)
{
double mid=(l+r)/2.0;
//cout<