# 交换茸角 状态DP 思路清奇

交换茸角 状态DP 思路清奇


问题:
动物园里有 $n $头麋鹿。每头麋鹿有两支茸角,每支茸角有一个重量。

然而,一旦某头麋鹿上两支茸角的重量之差过大,这头麋鹿就会失去平衡摔倒。为了不然这种悲剧发生,动物园院长决定交换某些茸角,使得任意一头麋鹿的两角重量差不超过$ c$。
然而,交换两支茸角十分麻烦,不仅因为茸角需要多个人来搬运,而且会给麋鹿造成痛苦。因此,你需要计算出最少交换次数,使得任意一头麋鹿的两角重量差不超过 (c)

注意,交换两支茸角只能在两头麋鹿之间进行。因为交换同一头麋鹿的两支角是没有意义的。


解:
这道题思路真的不容易想到
首先我想了半天 发现普通的状态无法推导

然后我看了一眼题解 还是 感觉 思路真的难想到

首先 我们应该先抛开 最小交换的条件 而去考虑不满足条件的因素
我们贪心一下
显然就是 排完序后 两两 之差 (>c)
满足条件的情况 就是 两两之差 (<=c)

然后我们考虑 状态压缩 首先我们先设 f[i]为 集合i 里面都满足条件的最小步数

  • 对于集合里面不满足条件的 显然是 inf
  • 对于集合里面满足条件的 是0
  • 而 满足条件但是原来不满足的 设为集合个数 sum-1

(f[i]=min(f[j]+)(f[i)^(j]));

考虑区间合并

这道题另一个思路清奇的地方
为什么这么做是对的,所以 我们可以对于那些需要全部交换的,答案就是sum-1
而对那些有的不需要交换的 可以通过子集递推

code:

//
#include<bits/stdc++.h>
using namespace std;
#define maxnn 1000000
int n;
int a1[maxnn],a2[maxnn];
int tmp[maxnn];
int tot=0;
int f[maxnn];
int all=0;
#define inf 10000000
int c;
int main()
{
	cin>>n>>c;
	all=(1<<n)-1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a1[i]);
		scanf("%d",&a2[i]);
	}
	for(int s=0;s<=all;s++)
	{
		tot=0;
		int ty=0;
		int fla1=1;
		for(int i=1;i<=n;i++)
		{
			if(s&(1<<i-1))
			{
				ty++;
				tmp[++tot]=a1[i];
				tmp[++tot]=a2[i];
				if(abs(a1[i]-a2[i])>c) fla1=0;
			}
		}
		sort(tmp+1,tmp+1+tot);
		int fla=1;
		for(int i=tot;i>=2;i-=2)
		{
			if(tmp[i]-tmp[i-1]>c)
			{
				fla=0;
			}
		}
		if(fla==0) f[s]=inf;
		else 
		if(fla1==1) f[s]=0;
		else
		f[s]=ty-1;
	}
	for(int s=0;s<=all;s++)
	{
		for(int i=s;i;i=(i-1)&s)
		{
			int k=s^i;
			f[s]=min(f[s],f[i]+f[k]);
		}
	}
	if(f[all]!=inf) cout<<f[all];
	else cout<<-1;
	
	
	
}

刀剑映出了战士的心。而我的心,漆黑且残破
原文地址:https://www.cnblogs.com/OIEREDSION/p/11454743.html