[TCO2016 Round 2B DIV1 Easy] TriangleTriples

[TCO2016 Round 2B DIV1 Middle] FoxAndGemstone

Zarxdy34 posted @ 2017年12月08日 02:10 in TopCoder with tags 数位DP , 328 阅读

    题意:有n个背包,每个背包里有若干个宝石,宝石最多有16种,用'A'~'P'来表示。每个背包中所有的宝石用一个字符串来表示。已知每种宝石重量都是不等的正实数,且可以通过一个天平比较两种宝石哪个更重。询问对于任意情况下(对于任意的宝石重量的排列),能否都判断出n个背包中最重的背包是哪个(相同重量的背包都算最重的)。

    题解:首先去除那些完全一样的背包。接下来我们计算对于每一个背包,其为重量最大的背包的16个宝石的重量排列方案数,如果方案数之和为\(16!\)则为Possible,否则为Impossible。这个方案数的统计利用了这个性质:背包a比背包b重的重要条件为\(cnt[a][j]>=cnt[b][j]\),其中\(cnt[x][j]\)表示的是背包x中重量大于等于j的宝石数目。

    首先枚举重量最大的背包。那么接下来我们可以枚举当前最小的宝石,利用数位dp进行状态转移。用16位二进制数S表示当前选出的最重的若干个宝石,\(f[S]\)表示方案数,显然\(f[S]>0\)仅当当前背包选出的S中的宝石数量大于等于其他背包中选出的S中的宝石数量。若S中包含第x个宝石,设x为S中最小的,则\(f[S]\)肯定可以由\(f[S\setminus x]\)中的所有方案末尾加上宝石x转移而来,据此计算f[S]。

    时间复杂度\(O(n2^mm)\),其中\(m=16\)。

 

#include <bits/stdc++.h>
using namespace std;

int num[50][16];
int valid[50][65536];
int q[65536];
long long f[65536];
int n;

int Cnt2_1(int x)
{
	int res=0;
	while (x)
	{
		res+=x&1;
		x>>=1;
	}
	return res;
}

bool cmp(const int &a,const int &b)
{
	return Cnt2_1(a)<Cnt2_1(b);
}

typedef vector <string> :: iterator it;

class FoxAndGemstone {
public:
	string isPossible(vector <string> bags) {
		for (it i=bags.begin();i!=bags.end();i++)
			sort(i->begin(),i->end());
		sort(bags.begin(),bags.end());
		int n=unique(bags.begin(),bags.end())-bags.begin();
		memset(valid,0,sizeof(valid));
		memset(num,0,sizeof(num));
		for (int i=0;i<n;i++)
		{
			for (int j=0;j<bags[i].length();j++)
				num[i][bags[i][j]-'A']++;
		}
		for (int i=0;i<65536;i++)
		{
			int cnt[50];
			memset(cnt,0,sizeof(cnt));
			for (int j=0;j<16;j++) if (i&(1<<j))
			for (int k=0;k<n;k++)
					cnt[k]+=num[k][j];
			int maxv=*max_element(cnt,cnt+n);
			for (int j=0;j<n;j++) if (cnt[j]==maxv) valid[j][i]=1;
		}
		for (int i=0;i<65536;i++) q[i]=i;
		sort(q,q+65536,cmp);
		long long ans=1;
		for (int i=2;i<=16;i++) ans*=(long long)i;
		for (int i=0;i<n;i++)
		{
			memset(f,0,sizeof(f));
			f[0]=1;
			for (int j=0;j<65536;j++) 
			{
				int x=q[j];
				if (!valid[i][x]) continue;
				for (int k=0;k<16;k++) if (x&(1<<k))
					f[x]+=f[x^(1<<k)];
			}
			ans-=f[65535];
		}
		printf("%lld\n",ans);
		if (ans==0) return "Possible";else return "Impossible";
	}
};

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter