2019牛客国庆集训派对day3 排列(状压dp)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019牛客国庆集训派对day3 排列(状压dp)相关的知识,希望对你有一定的参考价值。

LINK

状压每个位置是否被填充数字

考虑从小到大把数字填充进去,这样就可以去掉绝对值符号

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
const int maxn = 1<<21;
int n,m,a[maxn],b[maxn],z[maxn],y[maxn];
int bit[maxn],sumz[maxn],sumy[maxn],p[22],f[maxn];
signed main()
{
	bit[0] = 0;
	for(int i=1;i<maxn;i++)	bit[i] = bit[i>>1]+(i&1);
	while( cin >> n >> m )
	{
		for(int i=1;i<=n;i++)	cin >> p[i];
		for(int i=1;i<=m;i++)
		{
			cin >> a[i] >> b[i];
			z[a[i]] |= (1<<(b[i]-1));
			y[b[i]] |= (1<<(a[i]-1));
			sumz[a[i]]++, sumy[b[i]]++;
		}
		sort( p+1,p+1+n );
		int mx = 1<<n;
		for(int i=1;i<mx;i++)	f[i] = inf;
		for(int i=0;i<mx;i++)//枚举状态
		for(int j=1;j<=n;j++)//枚举哪个位置被放了数字
		{
			if( i&(1<<(j-1)) ) 
			{
				//当前放置的数字是剩余数字中最小的
				int t = i-(1<<(j-1)), v = p[bit[i]];
				int zuo = i&z[j];//作为左边的时候,有多少比自己小的数在右边 
				int you = i&y[j];//作为右边的时候,有多少个数在左边 
				int temp = v*( bit[zuo]+bit[you] )-v*( sumz[j]+sumy[j]-bit[zuo]-bit[you] ); 
				f[i] = min( f[i],f[t]+temp );
			}	
		}
		cout << f[mx-1] << endl;	
		for(int i=0;i<=n;i++)	z[i] = y[i] = sumz[i] = sumy[i] = 0;	
	}
}

以上是关于2019牛客国庆集训派对day3 排列(状压dp)的主要内容,如果未能解决你的问题,请参考以下文章

2019牛客国庆集训派对day3 J.买一送一(dfs+组合数学)

2019牛客国庆集训派对day1 D.Modulo Nine(巧妙的dp)

2019牛客国庆集训派对day1 F.4 Buttons(思维)

2019牛客国庆集训派对day1 H.有向图(高斯消元)

2019牛客国庆集训派对day2 C.Just h-index(主席树)

2019牛客国庆集训派对day2 J.Vertex Cover(思维,组合数学算贡献)