hdu 调查问卷(暴力状压入门)

题目

这道题目是典型的状压问题,但是还不至于用DP,题目也没让求最优方案,爆搜就行,用两种方法

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5;
int val[N];
typedef map<int, int> R;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T, n, m, k, cnt = 0;
    R r;
    R::iterator it;
    cin >> T;
    while (T--)
    {
        cnt++;
        string s;
        cin >> n >> m >> k;
        memset(val, 0, sizeof(val));
        for (int i = 1; i <= n; i++)
        {
            cin >> s;
            for (int j = 0; j < s.size(); j++)
            {
                if (s[j] == 'A')
                {
                    val[i] |= (1 << j);//将所有的位拆开保存
                }
            }
        }
        long long res = 0;
        for (int S = 0; S < (1 << m); S++)
        {
            int ans = 0, s = 0;
            r.clear();
            for (int i = 1; i <= n; i++)
                r[val[i] & S]++;//用s=100,样例为AAB,BAB,ABA,ABB,这样就得到了r[0]=1,r[1]=3,不就是有三对吗,因为s不同所以下标的也不一定相邻,用map好多了
            for ( it = r.begin(); it != r.end(); it++)//map会将其下标从小到大排序
            {//想一想,如果有不同的,不就能凑出一对吗(在s的条件下,也就是集合为s时)
                ans += (s*it->second);//如果说r[0]=0,那么ans就一直为0,没有不同的肯定就不能凑成出一对不同的呗
                s += it->second;//这应该算是组合数学里面的公式,求组合数的
            }
            if (ans >= k)
                res++;
        }
        printf("Case #%d: %d\n", cnt, res);
    }
    return 0;
}

上面的可能不太直观的得到,看第二种,转载:https://blog.csdn.net/qq_41508508/article/details/81538319

//
//  main.cpp
//  P1001
//
//  Created by jinyu on 2018/8/9.
//  Copyright © 2018年 jinyu. All rights reserved.
//
 
#include <iostream>
using namespace std;
const int MAXN = 1000+7;    //问卷份数的最大值 1000
const int MAXS = 1024+7;    //状态最大值为 2的10次方
 
char s[17];
int value[MAXN];    //value[i] 表示第i份问卷的值,A用 “1” 表示,B用 “0” 表示
int visit[MAXS];    //visit[i] 表示在当前问题集合的情况下,状态i出现的次数
int dp[MAXN][MAXS];     //dp[i][state]表示当前state状态在前i份问卷一共有对多少不同
 
int main(){
    int T;
    scanf("%d",&T);
    int tt = T;
    while(tt--){
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1;i<=n;i++){
            value[i] = 0;
            scanf("%s",s+1);
            for(int j = 1;j<=m;j++){
                if(s[j]=='A'){
                    value[i]+=(1<<(m-j));
                }
            }
        }
        printf("Case #%d: ",T-tt);
        if(n*(n-1)/2 < k)   printf("0\n");  //n取2的组合数一共有 n*(n-1)/2 种
        else{
            memset(dp, 0, sizeof(dp));
            int ans = 0;
            
            //state表示选中的问题集合,1则代表该问题被选中
            //通过按位与&运算,state & value[j] 得到当前state问题集合下问题的答案
            //按位与运算的应用:可以获取指定位的数值,如:x&100101 可以得到x的第1,4,6位的数值
            //状态转移:dp[j][state] = dp[j-1][state] + (j-1) - visit[nowValue];
            // dp[j][state]: 假定第j份和前面j-1份都不同,则可以产生j-1对不同的问卷,加上dp[j-1][state],然后去掉重复的,即减去visit[nowValue]
            
            for(int state = 1;state<(1<<m);++state){    //问题集合至少选1个问题,至多选全部问题
                memset(visit, 0, sizeof(visit));
                for(int j = 1;j<=n;j++){
                    int nowValue = state & value[j];    //得到当前state问题集合下问题的答案
                    dp[j][state] = dp[j-1][state] + (j-1) - visit[nowValue];
                    visit[nowValue]++;
                }
                if(dp[n][state]>=k) ans++;
            }
            printf("%d\n",ans);
        }
        
    }
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/81543425