CF611H New Year and Forgotten Tree

首先注意到:任何合法方案一定能调整成:每种位数选一个关键点,每条边都至少有一个关键点。

本质上是希望找一个边和点的匹配。

一种思路是确定关键点之间形成的树后(暴力枚举),让每条边匹配一个关键点。

但关键点是没有连边限制的,所以是要和有限个非关键点匹配。建立二分图匹配的网络流模型:源点向左部的边连 cnt 的流量,每条边向右部的两个点连一条边,点向汇连 cnt 的流量。

另一种思路是发现可以用 Hall 定理判断状态是否合法:确定根后是边和儿子的匹配,任意点集不小于边集即可。枚举所有本质不同的方案。

实际上不需要确定根,只需要保证点集大小大于边集大小。只需要不断枚举一种边加入判断是否合法。

时间复杂度都是能过。

思路二的代码:

#include <bits/stdc++.h>
using namespace std;
const int M=6;
int n,m,p[M],s[M],a[M][M];
int cnt(int x) {return x?cnt(x/10)+1:0;}
bool chk(){
	for(int i=0;i<1<<m;i++){
		int v=0,e=0;
		for(int j=0;j<m;j++) if(i>>j&1) v+=s[j];
		if(!v) continue;
		for(int j=0;j<m;j++) if(i>>j&1) for(int k=0;k<m;k++) if(i>>k&1) e+=a[j][k];
		if(v<=e) return 0; 
	}
	return 1;
}
bool upd(){
	for(int i=0;i<m;i++) for(int j=0;j<m;j++) if(s[i]&&(a[i][j]||a[j][i])){
		int&v=a[i][j]?a[i][j]:a[j][i];
		v--;s[i]--;
		if(chk()) return printf("%d %d\n",p[i]+s[i],p[j]),1;
		v++;s[i]++;
	}
	return 0;
}
int main(){
	cin.tie(0)->sync_with_stdio(0);
	cin>>n;
	m=cnt(n);p[0]=1;
	for(int i=1;i<=n;i++) s[cnt(i)-1]++;
	for(int i=1;i<m;i++) p[i]=p[i-1]*10;
	for(int i=1;i<n;i++){
		string u,v;
		cin>>u>>v;
		a[u.size()-1][v.size()-1]++;
	}
	if(!chk()) return puts("-1"),0;
	while(upd());
	return 0;
} 
posted @ 2023-02-24 09:18  shrtcl  阅读(14)  评论(0编辑  收藏  举报