1. 程式人生 > >2018 百度之星資格賽 1001 調查問卷

2018 百度之星資格賽 1001 調查問卷

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6344

題意:給n份問卷,每個問卷m道題,每題只有A,B兩種選項,問存在多少個問題集合,使得只保留這些問題後至少k對卷子不同。 

題解:剛開始做的時候,沒有什麼思路,因為要求至少K對,我對這個點無從下手,後來比賽結束看了下別人的部落格,原來可以用一個叫做狀態壓縮dp的東西來求解,用二進位制去列舉問題集合,有2^m種情況,

然後要確定狀態方程,dp[i][j]含義:對於前i個問卷,在問題集合j上,有多少對問卷不同

轉移狀態為:dp[i][j]=dp[i-1][j]+i-vis[tt];

其中,vis[tt]的含義:前i個問卷(包括i)中與第i個問卷中的問題集合狀態相同的數目

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <cmath>
#define maxn 1505
#define maxz 2005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int n,m,k;
int dp[maxn][maxn];
string s[maxn];
int vis[maxn];
int main()
{
	int t;
 	cin>>t;
 	int cnt=1;
 	while(t--)
 	{
		cin>>n>>m>>k;
		for(int i=1;i<=n;i++)
		{
			cin>>s[i];	
		}		
		for(int u=0;u<(1<<m);u++)
		{
			memset(vis,0,sizeof(vis));
			for(int i=1;i<=n;i++)
			{
				int tt=0;
				for(int j=0;j<m;j++)
				{
					if(u&(1<<j)&&s[i][j]=='A')
					{
						tt|=1<<j;
					}
				}
				vis[tt]++;
				dp[i][u]=dp[i-1][u]+i-vis[tt];
			}
		}
		LL ans=0;
		for(int i=0;i<(1<<m);i++)
		{
			if(dp[n][i]>=k)
			{
				ans++;
			}
		}
		cout << "Case #" << cnt++ << ": ";
		cout << ans << endl;
	}
	return 0;
}