T180745 菌落合并

n很小,肯定状压之类的题目
显然普通的01不能够确定菌落的状态
利用变进制数即可:\(00112\)可以表示\(12\ 34\ 5\)分别为一个菌落,这样刚好存的下。
转移也很简单,但是利用dfs递归会好写很多。当然还是顺推,逆推写不出。
具体实现的时候有很多妙方法优化复杂度,在代码里面可以体现。
变进制数也可以像二进制一样O(1)转移,非常方便。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
inline int r()
{
	int s=0,k=1;char c=getchar();
	while(!isdigit(c))
	{
		if(c=='-')k=-1;
		c=getchar();
	}
	while(isdigit(c))
	{
		s=s*10+c-'0';
		c=getchar();
	}
	return s*k;
}
string s;
int d;
int n,t,a[101],b[101],f[3700000],jc[101],pl[1001];
int dfs(int x)
{
	if(f[x]<1e18)return f[x];
	int th,tmp1;
	for(int i=0;i<n;i++)//合并第i个
	{
		if(i>0&&pl[i]==0)continue;//已经被合并 
		for(int j=i+1;j<n;j++)//和第j个 
		{
			if(pl[j]==0)continue;//已经被合并 
			int p1=pl[i],p2=pl[j];//回溯用
			int a1=a[i],a2=a[j];
			int tmp2=d;
			th=x;
			th-=pl[j]*j;
			th-=pl[i]*i;
			pl[i]+=pl[j];pl[j]=0;
			th+=pl[i]*i;//新的排列 
			d-=a[i]*a[i];
			d-=a[j]*a[j];
			a[i]+=a[j];a[j]=0;
			d+=a[i]*a[i];
			tmp1=d;//这次合并之后的代价 
			f[x]=min(f[x],dfs(th)+tmp1);
			a[i]=a1;a[j]=a2;
			pl[i]=p1;pl[j]=p2;
			d=tmp2;
		}
	}
	return f[x]; 
}
signed main()
{
	t=r();
	int x;jc[0]=1;
	for(int i=1;i<=10;i++)
	jc[i]=jc[i-1]*i;
	while(t--)
	{
		memset(f,0x7f,sizeof(f));
		n=r();
		d=0;
		for(int i=1;i<=n;i++)
		{
			x=r();
			cin>>s;
			if(s=="8B0012")x=-x;
			a[i-1]=x;
			d+=a[i-1]*a[i-1];
			pl[i-1]=jc[i-1];
		}
		f[0]=0;
		cout<<dfs(jc[n]-1)<<endl;
	}
}
posted @ 2021-07-25 16:06  lei_yu  阅读(90)  评论(0编辑  收藏  举报