【状压dp】connection

整数拆分后状态最多38000+,由于已经连的边不能用,所以把还剩多少边压进状态,也就700+条边,然后记g[s][k]为s状态,省k条边的数学期望,直接列公式转移,n==40显然是要打表的,话说这道题大丰中学出过一次,也依稀记得见过,结果最后还是没想出来...

把表全部打出来大概2~3分钟

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define ll long long
const int mo=10007;
using namespace std;
int a[41],n,next[2000000],ss,d[100];
int sora[50000][41];
bool g[38000][781];
long double f[38000][781];
void origin()
{
	for (int i=0;i<=mo;i++) next[i]=0;ss=mo;
	memset(g,0,sizeof(g));
}
bool cmp(int a[41],int b[41])
{
	if (a[0]!=b[0]) return 0;
	for (int i=1;i<=a[0];i++)
		if (a[i]!=b[i]) return 0;
	return 1;
}
int hash(int a[41])
{
	int h;
	for (int i=1;i<=a[0];i++) h=(h+a[i]*d[i])%mo;
	for (;next[h];) {
		h=next[h];
		if (cmp(sora[h-mo],a)) return h;
	}
	++ss,next[h]=ss,next[ss]=0;
	for (int i=0;i<=n;i++) sora[ss-mo][i]=a[i];
	return ss;
}
void dfs(int s,int a[41],int k)
{
	//cout<<s<<' '<<k<<endl; 
	//for (int i=1;i<=a[0];i++) cout<<a[i]<<' ';cout<<endl;
	g[s][k]=1;
	if (a[0]==1) {
		f[s][k]=0;
		return ;
	}
	int b[41];
	for (int i=0;i<=n;i++) b[i]=a[i];
	long double p=0;
	for (int i=1;i<=a[0];i++)
		for (int j=1;j<i;j++)
			p+=a[i]*a[j];
	f[s][k]=0;
	if (k>p) {
		if (!g[s][k-1]) dfs(s,a,k-1);
		f[s][k]+=(f[s][k-1]+1)*(k-p)/((long double)k);
		for (int i=0;i<=n;i++) a[i]=b[i];
	}
	for (int i=1;i<=a[0];i++) 
		for (int j=1;j<i;j++) {
			b[i]+=b[j],b[j]=mo;
			sort(b+1,b+b[0]+1);b[0]--;
			int ns=hash(b);
			if (!g[ns-mo][k-1]) dfs(ns-mo,b,k-1);
			for (int i=0;i<=n;i++) b[i]=a[i];
			f[s][k]+=(f[ns-mo][k-1]+1)*(a[i]*a[j])/((long double)k);
		} 
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	d[0]=1;
	for (int i=1;i<=50;i++) d[i]=(d[i-1]*131)%mo;
	for (n=1;n<=40;n++) {
		origin();
		int a[41]={n};
		for (int i=1;i<=n;i++) a[i]=1;
		int s=hash(a);
		dfs(s-mo,a,n*(n-1)/2);
		//printf("b[%d]=%d;\n",n,(int)f[s-mo][n*(n-1)/2]);
		cout<<"b["<<n<<"]="<<(int)f[s-mo][n*(n-1)/2]<<';'<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值