【匈牙利算法 最小字典序】距离序列


大致思路:

(先copy一个讲解过来)

这道题的关键在于对匈牙利算法过程的一个理解。

首先我们可以建立二分图的模型:每个位置可以有2种取值,于是我们把位置作为左边的点,取值作为右边的点。然后进行二分图匹配,只要有完美匹配,完美匹配就是一个可行解。

再考虑题面中最优性的要求。对于字典序问题,我们常常按照字典序枚举。于是这里也可以枚举:从上往下枚举左边的点,按照字典序枚举和右边的哪个点匹配,再看除开匹配了的两个点剩下的那个图中有没有完美匹配。举个例子:不妨设左边的点 uiu_iui 和右边的点 vi,vi′v_i,v_i'vi,vi 连了边。首先尝试匹配 (ui,vi)(u_i, v_i)(ui,vi) 。在除开了这条边以及这条边之前以及匹配的子图后,看剩下的图有无完美匹配。如果有,就选定 (ui,vi)(u_i,v_i)(ui,vi) ,否则尝试选定 (ui,vi′)(u_i, v_i')(ui,vi) 。如果 (ui,vi′)(u_i, v_i')(ui,vi) 还不行的话就说明无解。

由于枚举左边的点是 O(n)O(n)O(n) 的,匈牙利算法是 O(nm)=O(n2)O(nm)=O(n^2)O(nm)=O(n2) 的(边数是 O(n)O(n)O(n) 的),这个方法复杂度是 O(n3)O(n^3)O(n3) 的,有些高。

这时就要追寻匈牙利算法的本质了。不妨设左边点集为X,右边的为Y,那么匈牙利算法是后面的X点把Y点以前匹配的X点“挤掉”的过程,因为每次从某个X点 uuu 开始增广,都会试着让mat[v] = u。如果我们让字典序小的“挤掉”字典序大的,不就刚刚好可以满足题意了么?我们把vector内按照字典序输入,每次再倒着扫描,并且增广即可。复杂度为 O(n2)O(n^2)O(n2) ,但是上界比较松,可以通过。


用我的话

vector[i]代表Si所对应的(满足Di条件的)Ti,那么我们就从0开始从小到大来找这个Ti啊,这样就能保证vector[i]内是从小到大排列的,这样我们在用匈牙利的时候就是优先去连与更小的Ti的那根线咯。然后根据匈牙利算法过程,我们只要倒序地用匈牙利,就能让更小的Si对应的Ti尽量最小(Si说:我会尽量挤掉我的最小的Ti和比我大的Si的连线,除非这个比我大的Si只能连这个Ti别无他选)。


代码

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
vector<int> line[maxn];
int boy[maxn];
int girl[maxn];
int used[maxn];
int n;


bool dfs(int x) 
{
	for(int i=0;i<n;i++)
	{
		if(used[i]==0)
        {
            if(count(line[x].begin(),line[x].end(),i)!=0)
		    {
		    	used[i]=1;
			    if(boy[i]==0 || dfs(boy[i])==1)
		    	{
			    	boy[i]=x;
			    	girl[x]=i;
			    	return 1;
		    	}
		    }
        }
    }
	return 0;
} 




int main()
{
	cin>>n;
	int d;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&d); 
		for(int j=0;j<n;j++) //从小到大遍历来找符合要求的 
		{
			if(d==min(abs(i-j),n-abs(i-j)))
				line[i].push_back(j);
		}
	}


	for(int i=n-1;i>=0;i--) //这一步超级关键!要从大到小倒着用匈牙利! 
	{
		memset(used,0,sizeof(used));
		if(dfs(i)==0)
		{
			cout<<"No Answer";
			return 0;
		}
	}


	for(int i=0;i<n;i++)
	{
		if(i!=0)
			printf(" ");
		printf("%d",girl[i]);
	}
		
	return 0;
} 


我在调试过程中发现的问题:

①我蠢了!我最后输入boy[i] ,这个是指的Ti所对应的Si啊,我要输出的是Ti!所以专门用了一个girl[i]表示Si对应的T[i]。

(如果你不理解这种对应关系boygirl是什么鬼,请戳:https://blog.csdn.net/m0_38033475/article/details/79595617)

②最后一个样例运行超时,原因是我vector用的那个count函数可能时间复杂度有点高了,那么我就把

if(count(line[x].begin(),line[x].end(),i)!=0 && used[i]==0)
拆成两个if语句,先if(used[i]==0),进去之后再if count的那个,就优化成功了。



猜你喜欢

转载自blog.csdn.net/m0_38033475/article/details/80108294