洛谷P1963 变换序列 二分图匹配

给定一个 n ≤ 1 e 4 n\leq1e4 n1e4的序列,然后给出定义 D ( x , y ) = m i n { ∣ x − y ∣ , n − ∣ x − y ∣ } D(x,y)=min\{|x-y|,n-|x-y|\} D(x,y)=min{xy,nxy},现在给出了 D ( i , T ( i ) ) D(i,T(i)) D(i,T(i)),要求出一个变换后最小的全排列。
直接对每个 i i i连上所有的连边,理论上最多两种,枚举四种并且判定就可以了。然后跑二分图匹配,如果最大匹配小于 n n n,说明这种情形无解。否则直接输出结果。
注意二分图匹配是后者暴力替换前者,所以想要字典序最大需要倒序遍历。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e4+7;
int d[N],tim=0;
int n;  
int link[N],used[N],link2[N];
vector<int> G[N];
bool check(int x,int y,int d) {
	return min(abs(x-y),n-abs(x-y))==d;
}

int dfs(int u) {
	for(auto &v:G[u]) {
		if(used[v]==tim) continue;
		used[v]=tim;
		if(link[v]==-1||dfs(link[v])) {
			link[v]=u;
			link2[u]=v;
			return 1;
		}
	}
	return 0;
}

int solve() {
	int ans=0;
	memset(link,-1,sizeof(link));
	for(int i=n;i>=0;i--) {
		tim++;
		if(dfs(i)) ++ans;
	}
	return ans;
}
int main() {
	scanf("%d",&n);
	for(int i=0;i<n;i++) 
		scanf("%d",&d[i]);
	for(int i=0;i<n;i++) {
		if(i>=d[i]&&check(i,i-d[i],d[i])) G[i].push_back(i-d[i]);
		if(i+d[i]<n&&check(i,i+d[i],d[i])) G[i].push_back(i+d[i]);
		if(i>=n-d[i]&&check(i,i-(n-d[i]),d[i])) G[i].push_back(i-(n-d[i]));
		if(i+(n-d[i])<n&&check(i,i+(n-d[i]),d[i])) G[i].push_back(i+(n-d[i]));
	}
	for(int i=0;i<n;i++) 	
		sort(G[i].begin(),G[i].end());
	int ans=solve();
	if(ans<n) puts("No Answer");
	else {
		for(int i=0;i<n;i++) 
			printf("%d%c",link2[i],i==n-1?'\n':' ');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值