据说这题是个状压dp...然后看着刚捋清楚的套路,仍然懵逼。。。
同样先贴大佬博客Orz:https://blog.csdn.net/qq_37591656/article/details/81427818
http://www.cnblogs.com/LQLlulu/p/9419232.html
突然就明白过来这题要干嘛了。。sta数组并不是必须的。。甚至连cur数组也不是必须的。。。
之前都是先利用sta数组剪枝或优化,再从中找符合条件的状态进行转移,但是这道题sta并没有什么限制,就是000...~111...的问题集合,表示这题选还是不选。
dp[i][S]表示前i个问卷,问题集合(选题情况)为S,不同的试卷对数
转移方程为:dp[i][s]=dp[i-1][S]+i-num[state] //state=S&cur[i]; 表示答案情况
num数组表示前i份答卷里有多少和第i个问卷在选题情况为S的情况下,答案相同的问卷数目。
附上AC代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const int MAXM=12;
const int MAX=1<<12;
int n,m,k;
int cur[MAXN];//记录每一行的状态
int dp[MAXN][MAX];
int num[MAX];
int main()
{
int t;
scanf("%d",&t);
for(int tt=1;tt<=t;tt++)
{
memset(cur,0,sizeof(cur));
memset(dp,0,sizeof(dp));
scanf("%d%d%d",&n,&m,&k);
char s[MAXM];
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='A')
cur[i]+=(1<<(m-j));
}
printf("Case #%d: ",tt);
/*if(k>(n*(n-1)/2))
{
printf("0\n");
continue;
}*/
for(int st=0;st<(1<<m);st++)
{
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
int state=st&cur[i];
num[state]++;//注意先自增
dp[i][st]=dp[i-1][st]+i-num[state];
}
}
int ans=0;
for(int st=0;st<(1<<m);st++)
if(dp[n][st]>=k)
ans++;
printf("%d\n",ans);
}
return 0;
}
我好菜啊QAQ...dp还是没掌握到精髓,状态转移方程总是写不出来55555...