我的第一个博客:2018百度之星1001资格赛调查问卷度度熊

我是acm的小菜鸡,为了帮助菜鸡一起成长,也为了提升自己的菜鸡水平,今天开始定期发布一些自己解题的思考与感悟,期望会对你们有所帮助奥~~
今天带来的是百度之星2018的资格赛题目第一题:调查问卷

Time Limit: 6500/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Problem Description
度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 m 个问题的调查问卷,每个问题只有 ‘A’ 和 ‘B’ 两种选项。 将问卷散发出去之后,度度熊收到了 n 份互不相同的问卷,在整理结果的时候,他发现可以只保留其中的一部分问题,使得这 n 份问卷仍然是互不相同的。这里认为两张问卷是不同的,当且仅当存在至少一个被保留的问题在这两份问卷中的回答不同。 现在度度熊想知道,存在多少个问题集合,使得这 n 份问卷在只保留这个集合的问题之后至少有 k 对问卷是不同的。

Input
第一行包含一个整数 T,表示有 T 组测试数据。 接下来依次描述 T 组测试数据。对于每组测试数据: 第一行包含三个整数 n,m 和 k,含义同题目描述。 接下来 n 行,每行包含一个长度为 m 的只包含 ‘A’ 和 ‘B’ 的字符串,表示这份问卷对每个问题的回答。 保证 1≤T≤100,1≤n≤103,1≤m≤10,1≤k≤106,给定的 n 份问卷互不相同。

Output
对于每组测试数据,输出一行信息 “Case #x: y”(不含引号),其中 x 表示这是第 x 组测试数据,y 表示满足条件的问题集合的个数,行末不要有多余空格。
Sample Input
Copy
2
2 2 1
AA
BB
2 2 2
AA
BBSample Output
Copy
Case #1: 3
Case #2: 0
1.题目分析:此题因为每一道题目的内容会有选中与不选中两种情况,所以可以联想到用状态压缩的状态来表示每一道题目的情况。
**首先给大家介绍一下状态压缩的相关知识:
<<左移符号,比如1<<3将数字1左移三位,相当于二进制数1000,>>右移符号,比如1000>>3,将1000向右移动3位,结果为1
&运算符,a&b表示a的位上的每个数字与b做&运算,如果同一位上两个数字都为1结果为1,否则该位上结果为零。
|运算符,a|b表示a的位上每个数字与b做|运算,如果同一位上两个数字结果都为零时结果为零,否则结果为1
接下来分析一些复杂的二进制运算
if(((1<<(i-1))&x)>0):判断第i位上的数字是否为1
1<<(i-1),将1左移i-1位,相当于制造了一个只有第i位上的数字是1,其他位上都是0的二进制数,然后与x做与运算,如果结果>0,说明第i位上的数字为1,否则该位上数字为0。接下来将这个数字与x做&运算,由于左移会将空位补零,所以1<<(i-1)其他位上的数字都为0,只有第i位上的数字为1,做与运算后如果结果>0,说明第i位上是1,反之则结果是零。
x = x|(1<<(i-1)):1<<(i-1)得出第i位为1其他位数字为0的数字,做或运算之后把x的第i位数字改为1
首先我们要循环选中每种题目的组合类型,然后我们需要记录下每一张试卷对于选中题目的答案,我们开一个数组vis记录每种解题方案的个数,这里选A记录为1,选B记录为0,开一个二重数组paper,第一个数字代表该试卷是第几张试卷,第二个数字代表选题的方案,该二重数组这张试卷之前与它解题不同的试卷对数,有递推公式
paper[i][sta] = paper[i-1][sta] +i-vis[S];
i为第几张试卷,vis[S]中S为当前选题方案下该试卷的解题过程,vis[S]记录这种方案的相同试卷个数,所以i-vis[S]即为该试卷的此种解题方案新产生的与前面试卷不同对数,再加上前面一张试卷这种选题方案下的不同对数,得出的就是这种方案下到这张试卷之前产生的不同试卷对数,最后查找大于k的不同方案试卷个数即可


写出伪代码:
for(循环方案个数)
{
    for(循环试卷张数)
    {
        for(循环每道题目)
        {
            将答案数组置零
            if(该题目在该方案中选中)
            {
                if(该卷的答案为A)
                {
                    该位用1记录
                }
            }
        }
        将这种解题答案对应数组的值加一
    }
    该试卷不同对数 = 前一张试卷不同的对数+该试卷新产生的试卷对数
}
for(循环试卷答案数组)
{
    记录不同对数>k的个数
}
输出
#include  <iostream>
#include  <cstdio>
#include  <algorithm>
using  namespace  std;
int  main()
{
    int  T;
    string  s[11];
    scanf("%d",&T);
    int  vis[1<<11];
    int  S;
    for(int  u=0;u<T;u++)
    {
        int  n,m,k;
        scanf("%d %d %d",&n,&m,&k);
        for(int  i=1;i<=n;i++)
        {
            cin>>s[i];
        }
        int  paper[n+1][1<<m];
        for(int  i=0;i<=n;i++)
        {
            for(int  j=0;j<(1<<m);j++)
            {
                paper[i][j] = 0;
            }
        }
        for(int  sta=1;sta<(1<<m);sta++)
        {//遍历题目的种类
            fill(vis,vis+(1<<11),0);
            for(int  i=1;i<=n;i++)
            {//遍历试卷
                S = 0;
                for(int  j=0;j<m;j++)
                {//遍历题目
                    if(((1<<j)&sta)>0 &&
                       s[i][j] == 'A')
                    //第j+1位选中并且这j题的答案为A的情况下
                    {
                        S = S|(1<<j);
                    }//选中并且这题答案为A的话
                }
                vis[S] ++;
                paper[i][sta] = paper[i-1][sta]+i-vis[S];
            }
        }
    int  ans=0;
    for(int  i=0;i<=(1<<m);i++)
    {
            if(paper[n][j] >= k)
            {
                ans ++;
            }

    }
    printf("%d\n",ans);
}
return  0;

}

发布了17 篇原创文章 · 获赞 7 · 访问量 3005

猜你喜欢

转载自blog.csdn.net/znevegiveup1/article/details/81476160
今日推荐