Problem Description
度度熊爲了完成畢業論文,需要收集一些數據來支撐他的論據,於是設計了一份包含 mm 個問題的調查問卷,每個問題只有 'A' 和 'B' 兩種選項。
將問卷散發出去之後,度度熊收到了 nn 份互不相同的問卷,在整理結果的時候,他發現可以只保留其中的一部分問題,使得這 nn 份問卷仍然是互不相同的。這裏認爲兩張問卷是不同的,當且僅當存在至少一個被保留的問題在這兩份問卷中的回答不同。
現在度度熊想知道,存在多少個問題集合,使得這 nn 份問卷在只保留這個集合的問題之後至少有 kk 對問卷是不同的。
Input
第一行包含一個整數 TT,表示有 TT 組測試數據。
接下來依次描述 TT 組測試數據。對於每組測試數據:
第一行包含三個整數 nn,mm 和 kk,含義同題目描述。
接下來 nn 行,每行包含一個長度爲 mm 的只包含 'A' 和 'B' 的字符串,表示這份問卷對每個問題的回答。
保證 1 \leq T \leq 1001≤T≤100,1 \leq n \leq 10^31≤n≤103,1 \leq m \leq 101≤m≤10,1 \leq k \leq 10^61≤k≤106,給定的 nn 份問卷互不相同。
Output
對於每組測試數據,輸出一行信息 "Case #x: y"(不含引號),其中 x 表示這是第 xx 組測試數據,y 表示滿足條件的問題集合的個數,行末不要有多餘空格。
Sample Input
2
2 2 1
AA
BB
2 2 2
AA
BB
Sample Output
Case #1: 3
Case #2: 0
這個題目讓我很慌...看了好久沒看懂題目...然後用map打完又顯示超時...這樣讓我很煩...前前後後改來改去提交超時了四次, 最後改寫法才成功AC.
這個題目就是一個找字串,然後判斷字串對數大於k的情況有多少種, 最後輸出有多少種情況...
對於具體如何遍歷有多少種組合 ,想必這個不用我說, 定義一個end = (1<<m)-1就行, 然後遍歷所有問題, 利用位運算選出被選中的問題是哪幾個.
最初我是額外定義一個char字符數組, 然後判斷當前字符串是否已經出現過, 如果沒有出現過, 說明前面的每一個字符串都可以和它組成一對, 假設當前遍歷到第i個數組, 那麼在這個地方就可以新出現 i-1對, 反之如果這個字符數組出現過, 那麼利用map.count得到它之前出現的次數cnt, 說明前面有cnt個字符串和它不能組成一對, 那麼說明在前面能和當前字符串組成一對的字符串有j - cnt個. 這樣一直遍歷到最後,如果對數大於K, 那麼答案+1.
這個是最初的想法, 驗證是錯誤, 但是超時我就很煩...直到後面ctrl+a and delete重寫一遍...
因爲該題目中的問題只會有兩種結果A 和 B, 如果將A變成0,B換成1, 那麼它就變成了一個01串,並且每一張答卷因爲B出現的位置或者次數不同, 會有唯一的一個數代表它, 這樣,我們就可以用一個整數來代表一個字符串.
在後面遍歷每個字串的時候, 首先判斷這個問題是否已經被選取, 如果說沒有被選取, 那麼就判斷這個點是否爲B, 因爲在前面已經將A和B改成0和1了, 所以我們就判斷這個問題的結果是否爲1, 如果這個問題的結果爲1, 就利用一個整數將這個爲1的位置記錄下來
下面舉個栗子
假如說字符串是 0 0 0 1 1 1;
我們要選取的是 0 0 0 1 0 1;
然後定義一個整數 0 0 0 0 0 0
從最左邊往右一直判斷, 這個位置是否被選取,同時這個位置是否爲B,
這樣就會得到結果 0 0 0 1 0 1 = 5,於是5就代表這個字符串, 其他任意的字符串結果都不會得到5這個數值.
同時注意到最大的位數爲10, 所以可以額外定義一個1100的整數數組, 初始化全爲0
記錄下每個數出現的次數, 也就是每種字符串出現的次數.
然後將每一種和另外的幾種連線, 爲了防止計算兩次,
就定義i從0 到 1024進行遍歷, 初始有n個點
到遇到第一個不爲0的數的時候, 每個這個字符串能夠和後面n - sz[i]個字符串組成1對
所以就會有sz[i] * ( n-sz[i] )種方案. 後面的同理
下面貼代碼:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
const int maxn = 1005;
int t,n,m,k;
char sz[maxn][12];//存儲所有的字符串
int cnt[2005];
char tp[12];
map<int,int> mp;
int main(){
cin>>t;
int e,i,j,c;
for(c=1;c<=t;c++){
scanf("%d %d %d",&n,&m,&k);
for(i=0;i<n;i++){
for(j=0;j<m;j++){
scanf(" %c",&sz[i][j]);
sz[i][j] -= 'A';
}
}
//將字符串改成01串
e = (1<<m)-1;
int tmp,ttmp,tans,p,sum=0,ans=0,g=0;
for(i=1;i<=e;i++){
memset(cnt,0,sizeof(cnt));
for(j=0;j<n;j++){
tmp=1,tans=0,ttmp=1;
for(g=0;g<m;g++,tmp<<=1){ // 遍歷字符的每一位
if(sz[j][g] && (i&tmp)){
tans |= tmp;
}
}
//tans是這個字符串轉數字之後的結果
cnt[tans]++;
}
ttmp=0,sum=0;
for(j=1;j<=1024;j++)
if(cnt[j]){ // 如果這個點不爲0的話
ttmp += cnt[j];
sum += (n-ttmp)*cnt[j];
}
if(sum >= k) ans++;
}
printf("Case #%d: %d\n",c,ans);
}
return 0;
}