练习09-三种匹配算法(模板)

一、匈牙利算法

 解决二分图中的最大匹配问题(很多时候都可以把题目转变成二分图,刚学网络流有点头大)。两个互不相交的子集V1 ,V2 寻找他们能匹配到的最大数量。(由于我菜鸡,仅仅给出几种模板,网上有很多博客讲的很好了)

1、邻接表实现(O( n^3)

模板秒切

 过山车

【模板】二分图匹配

[ZJOI2009]假期的宿舍

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1001;
int n1,n2,k;
//n1,n2为二分图的顶点集,其中x∈n1,y∈n2
int map[N][N],vis[N],link[N];
//link记录n2中的点y在n1中所匹配的x点的编号
int find(int x)
{
    int i;
    for(i=1;i<=n2;i++)
    {
        if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索
        {
            vis[i]=1;//标记节点已被搜索
            //如果i不属于前一个匹配M或被i匹配到的节点可以寻找到增广路
            if(link[i]==0||find(link[i]))
            {
                link[i]=x;//更新
                return 1;//匹配成功
            }
        }
    }
   return 0;
}
int main()
{
    int i,x,y,s=0;

    while(~scanf("%d",&k)&&k)
    {
    s=0;
    scanf("%d%d",&n1,&n2);
    memset(map,0,sizeof(map));
    memset(link,0,sizeof(link));
    for(i=0;i<k;i++)
    {
        scanf("%d%d",&x,&y);
        map[x][y]=1;
    }
    for(i=1;i<=n1;i++)
    {
        memset(vis,0,sizeof(vis));  //每次记得初始化
        if(find(i))
            s++;
    }
    printf("%d\n",s);
    }
    return 0;
}

2、邻接表实现(O(n*m))

一样的思路,不过是改成了邻接表,数据较大(直接记这个就好了)。

练习:
[SCOI2010]连续攻击游戏

[ZJOI2007]矩阵游戏

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<queue>
#define Max 500
#define inf 0x3f3f3f3f

using namespace std;
void show(int *a,int n);
struct Node{
	int next;
	int to;
}edge[Max*Max];
int num_edge;
int head[Max];
void add_edge(int x,int y)  //邻接表
{
	edge[++num_edge].next=head[x];
	edge[num_edge].to=y;
	head[x]=num_edge;
}
bool vis[Max];//是否匹配过了
int link[Max];
bool find(int num)   
{
	int i,u;
	for(i=head[num];i;i=edge[i].next)  //邻接表访问
	{
		u=edge[i].to;
		if(!vis[u])
		{
			vis[u]=1;
			if(!link[u]||find(link[u]))
			{
				link[u]=num;
				return true;
			}
		}
	 }
	return false;
}
int main()
{
	int t,n,cnt1,cnt2,x;
	scanf("%d",&t);
	while(t--)
	{
		memset(head,0,sizeof(head));
		memset(edge,0,sizeof(edge));
		memset(link,0,sizeof(link));
		num_edge=0;
		cnt1=0;
		cnt2=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&x);
				if(x)
				{
					cnt1++;
					add_edge(i,j+n); //行 
				}
			}
		}
		if(cnt1<n)
		{
			printf("No\n");
			continue;
		}
		for(int i=1;i<=n;i++)
		{
			memset(vis,0,sizeof(vis));
			if(find(i))
				cnt2++;
		}
		if(cnt2>=n)
			printf("Yes\n");
		else 
			printf("No\n");
	}
	return 0;
}

3、需要自己根据题目构建一个二分图

简单练习:

[SHOI2001]小狗散步

AC代码:

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<queue>
#include<math.h>
#define Max 450
#define inf 0x3f3f3f3f

using namespace std;
struct Node{
	int x;
	int y;
	int id;
	double dis;
}p[400],b[Max];
struct Edge{
	int next;
	int to;
}edge[Max*Max];
bool vis[Max];
int link[Max];
int head[Max];
int ans[Max];
int num_edge;
double get_dis(int x1,int y1,int x2,int y2)  //计算两点的距离
{
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void add_edge(int x,int y)
{
	edge[++num_edge].next=head[x];
	edge[num_edge].to=y;
	head[x]=num_edge;
}
bool find(int num)  //模板
{
	int i,u;
	for(i=head[num];i;i=edge[i].next)
	{
		u=edge[i].to;
		if(!vis[u])
		{
			vis[u]=1;
			if(!link[u]||find(link[u]))
			{
				link[u]=num;
				ans[num]=u;
				return true;
			}
		}
	}
	return false;
}

int main()
{
	int n,m,x,y,cnt;
	double d1,d2;
	struct Node temp;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&p[i].x,&p[i].y);
		p[i].id=i;
		if(i>1)  //获得距离
		{
			p[i-1].dis=get_dis(p[i-1].x,p[i-1].y,p[i].x,p[i].y); //计算前后两个目标点的距离
		}
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		b[i].id=i;
		b[i].x=x;
		b[i].y=y;
		for(int j=1;j<n;j++) //
		{
			d1=get_dis(x,y,p[j].x,p[j].y);
			d2=get_dis(x,y,p[j+1].x,p[j+1].y);
			if(d1+d2<=2*p[j].dis) //可以赶回来 就把他们连起来
			{
//				printf("%d %d %d\n",j,j+1,i);
				add_edge(j,n+i);
			}
		}
	}
	cnt=0;
	for(int i=1;i<=n;i++)
	{
		memset(vis,0,sizeof(vis));
		if(find(i))
			cnt++;
	}
	printf("%d\n",cnt+n);
	for(int i=1;i<=n;i++)
	{
		printf("%d %d",p[i].x,p[i].y);
		if(i!=n)
			printf(" ");
		if(i!=n&&ans[i])
		{
			printf("%d %d ",b[ans[i]-n].x,b[ans[i]-n].y);
		}
	}
	printf("\n");
	return 0;
}

二、KM算法

  求带权值的二分图的最优匹配(还是直上模板和题目,还是给个链接吧,网上貌似很多,图都一样。。。。)

KM算法介绍      百科

练习题:
奔小康赚大钱

模板:
 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<algorithm>
#define Max 305
#define inf 0x3f3f3f3f
#define max(a,b) a>b?a:b;

using namespace std;
int min(int x,int y)
{
	if(x>y) return y;
	else return x;
}
int love[Max][Max];//记录好感度
int e_girl[Max];//记录期望值
int e_boy[Max];
bool vis_girl[Max];	//女孩是否匹配过
bool vis_boy[Max];	//男孩是否匹配过
int match[Max];	//记录匹配到的对象,boy匹配到的girl
int slack[Max];   //记录需要增加或者减少的标记
int N; //最大的人数
bool dfs(int girl)  //匈牙利求匹配
{
	vis_girl[girl]=true;
	for(int i=0;i<N;i++)
	{
		if(!vis_boy[i])
		{
			int gap=e_girl[girl]+e_boy[i];
			if(gap==love[girl][i]) //如果标签和边的值相等,尝试匹配
			{
				vis_boy[i]=true;
				if(match[i]==-1||dfs(match[i])) //如果没有匹配或者可以找到其他人
				{
					match[i]=girl;
					return true;
				}
			}
			else
				{
					//还差多少能获得该对象,用于更新标签,也就是期望
					slack[i]=min(slack[i],gap-love[girl][i]); 
				}
		}
	}
	return false;
}
int KM()
{
	memset(match,-1,sizeof(match));
	memset(e_boy,0,sizeof(e_boy));
	for(int i=0;i<N;i++)
	{
		e_girl[i]=love[i][0]; //找到初始期望,所有边值最大的那个
		for(int j=1;j<N;j++)
		{
			e_girl[i]=max(e_girl[i],love[i][j]);
		}
	}
	for(int i=0;i<N;i++)
	{
		memset(slack,inf,sizeof(slack));  //每次刷新期望
		while(1)
		{
			memset(vis_girl,false,sizeof(vis_girl));
			memset(vis_boy,false,sizeof(vis_boy));
			if(dfs(i)) break;//找到一个pass
			int d=inf;
			for(int j=0;j<N;j++)
			{
				//找到没有匹配过最小的期望,更新,保证每次更新后能再次匹配
				if(!vis_boy[j]) d=min(d,slack[j]);
			}
			for(int j=0;j<N;j++)
			{
				if(vis_girl[j]) e_girl[j]-=d;  //女生降低期望
				if(vis_boy[j]) e_boy[j]+=d; //男生提高要求
				else slack[j]-=d; //没有访问过 因为女生期望降低,所以slack减少
			}

		}
	}
	int res=0;
	for(int i=0;i<N;i++)
	{
		res+=love[match[i]][i];
	}
	return res;
}
int main()
{
	while(scanf("%d",&N)!=EOF)
	{
		for(int i=0;i<N;i++)
		{
			for(int j=0;j<N;j++)
			{
				scanf("%d",&love[i][j]);
			}
		}
		printf("%d\n",KM());
	}
	return 0;
}

三、稳定婚姻匹配(GS算法)

核心就是不断找女朋友 (所以又叫女朋友算法,)

思路很简单,但是需要定义很多变量,反正就是一直找,直到全部遍历完成或者全部配对成功。

模板练习:
[国家集训队]稳定婚姻

Marriage is Stable

模板(使用了map记录名字):

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<iostream>
#include<algorithm>
#define Max 510
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;

using namespace std;
map<string ,int > boy;//因为名字可能相同,。。。
map<string ,int > girl;
map<string ,int >::iterator it;
string boyName[Max],girlName[Max];//男女的名字
int relation[Max][Max];
int manpos[Max];
int boyMatch[Max];
int girlMatch[Max];

int boyList[Max][Max];//男孩清单
int girlList[Max][Max];//女孩清单
int cnt;
int a[Max];
queue<int> q;//队列
int main()
{
    string temp1,temp2,temp;//转化字符
    int n;
    char name[100],name1[100];//名字输入
    while(scanf("%d",&n)!=EOF)
    {
        cnt=0;
        boy.clear(),girl.clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",name);
            temp1=name;
            boyName[i]=temp1;//记录
            boy[temp1]=i;
            for(int j=1;j<=n;j++)
            {
                scanf("%s",name1);
                temp2=name1;
                if(!girl[temp2])
                {
                    girl[temp2]=++cnt;
                    girlName[cnt]=temp2;
                }
                boyList[i][j]=girl[temp2];//转化为数字
                relation[i][girl[temp2]]=j;//i 到j 的优先级为 j
                
            }
        }
        for(int i=1;i<=n;++i)
        {
            scanf("%s",name);
            temp=name;
            int id=girl[temp]; //找到女孩的编号
            for(int j=1;j<=n;j++)
            {
                scanf("%s",name1);
                int boy_id=boy[name1];//
                girlList[id][j]=boy_id;
                relation[id][boy_id]=j;
            }
        }
        while(q.size()) q.pop();
        for(int i=1;i<=n;i++)
        {
            q.push(i);//把所有的男生全部压人
        }
        for(int i=1;i<=n;i++)
        {
            boyMatch[i]=girlMatch[i]=-1;//-1表示没有匹配
            manpos[i]=0;
        }
        while(q.size())
        {
            int s=q.size();//目前还有多少人没匹配
            for(int i=1;i<=s;i++)
            {
                int now=q.front();//获得男孩序号
                ++manpos[now]; //匹配次数增加
                q.pop();
                if(girlMatch[boyList[now][manpos[now]]]==-1)  //没男朋友
                {
                    girlMatch[boyList[now][manpos[now]]]=now;//匹配
                    boyMatch[now]=boyList[now][manpos[now]];// 记录
                }
                else //比较优先级
                {
                    if(relation[boyList[now][manpos[now]]][now]<relation[boyList[now][manpos[now]]][girlMatch[boyList[now][manpos[now]]]])
                    {
                        boyMatch[girlMatch[boyList[now][manpos[now]]]]=-1;//把前男友鸽了
                        q.push(girlMatch[boyList[now][manpos[now]]]);
                        girlMatch[boyList[now][manpos[now]]]=now;//和最新的在一起
                        boyMatch[now]=boyList[now][manpos[now]];
                    }
                    else  //还是和原来的在一起
                    {
                        q.push(now);//本次匹配失败
                    }
                }
            }
        }
        for(it=boy.begin();it!=boy.end();++it)
        {
            temp=it->first;
            temp1=girlName[boyMatch[it->second]];
            cout<<temp<<" "<<temp1<<endl;
        }
    }
    return 0;
}

  三种匹配算法基础模板都在这了,不过还需要更多的题目训练自己。(稳定婚姻算法还获得了2012诺贝尔经济学奖,感觉我也会,狗头~~~)

 感觉现在网络流和点分治一起看有点慢啊,在机房学习效率真低,还不如晚上自己一小时。加油吧 还有半个月,省选题目尽量多刷,树和图我都要!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ORB-SLAM是一种基于特征点的视觉SLAM算法,它使用ORB特征点来进行特征匹配。ORB特征点是一种既能够快速检测又能够准确描述图像特征的算法。ORB特征点的检测和描述分别使用了FAST角点检测算法和BRIEF描述子算法。ORB-SLAM使用了基于词袋模型的方法来进行特征匹配,具体来说,ORB-SLAM将所有的ORB特征点分成若干个不同的视觉词汇,然后使用BoW(Bag of Words)模型来描述每个图像。在ORB-SLAM中,特征匹配主要分为两个步骤:词袋匹配和光流法匹配。词袋匹配是通过计算两个图像的词袋向量之间的距离来进行的,而光流法匹配则是通过计算两个图像中的特征点在相邻帧之间的运动来进行的。 以下是一个ORB-SLAM特征匹配算法的示例代码: ```c++ // ORB特征点检测和描述 cv::Ptr<cv::ORB> orb = cv::ORB::create(); std::vector<cv::KeyPoint> keypoints1, keypoints2; cv::Mat descriptors1, descriptors2; orb->detectAndCompute(img1, cv::noArray(), keypoints1, descriptors1); orb->detectAndCompute(img2, cv::noArray(), keypoints2, descriptors2); // 词袋匹配 cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming"); std::vector<cv::DMatch> matches; matcher->match(descriptors1, descriptors2, matches); // 光流法匹配 std::vector<cv::Point2f> points1, points2; for (auto match : matches) { points1.push_back(keypoints1[match.queryIdx].pt); points2.push_back(keypoints2[match.trainIdx].pt); } std::vector<uchar> status; std::vector<float> err; cv::calcOpticalFlowPyrLK(img1, img2, points1, points2, status, err); // 输出匹配结果 for (int i = 0; i < matches.size(); i++) { if (status[i]) { cv::DMatch match = matches[i]; cv::Point2f pt1 = keypoints1[match.queryIdx].pt; cv::Point2f pt2 = keypoints2[match.trainIdx].pt; std::cout << "Match " << i << ": (" << pt1.x << ", " << pt1.y << ") -> (" << pt2.x << ", " << pt2.y << ")" << std::endl; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值