描述: 输入t,p,n,n个数的显示法
t 表示有几组测试数据,p表示有多少条显示条,n表示需要显示的数据有多少 (0<=p<=15 0<=n<=99)即有计步器能够表示两个数位的数0~99。
题意: p 条显示条中能否找到最少的条数便可表示 n 个数(即 找到坏了最多了显示条,也能区分这n个数的最少的没坏的显示条数)
用0~2^p的二进制中为1的位数表示该位数的显示条为好的,即用二进制数模拟显示条好坏,再枚举二进制......
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
int hash[100000]; // 2 ^ 15 = 32768 < 100000
int frac[20]; //fac[0 1 2 3 4 5 6 7 8 9 10…… ]
// [1 2^1 2^2 2^3 …… …… ] 便于将排列(二进制)化为十进制
int a[120][20];
using namespace std;
int main()
{
int t;
int p,n,num,sum;
int min;
frac[0]=1;
int i,j,k,ok;
for(i=1;i<20;i++)
{
frac[i]=2*frac[i-1];
}
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&p,&n);
for(i=0;i<n;i++)
{
for(j=0;j<p;j++)
{
scanf("%d",&a[i][j]);
}
}
min=1000000000;
int w=1;
for(i=0;i<p;i++)
{
w=w*2;
if(w+1>=n) {w=i;break;}
}
for(i=w;i<(1<<p);i++)
{
//最开始我从i=0开始(参考别人的)提交后超时,后来发现i至少得表示n位数即2^i>=n才行,所以在之
//前先for找到
//这个最小i,便可减少时间,提交后便通过了。
memset(hash,0,sizeof(hash));
num=0;
for(k=0;k<p;k++) // i 中为 1 的位置表示 用该位作为判不同的某列
if(i&(1<<k)) // 判断此时 i 中为1的位数
num++;
ok=1;
for(j=0;j<n;j++) //将 排列 为 i 的二进制对应的排列用1标志
{
sum=0;
for(k=0;k<p;k++)
if(i&(1<<k))
{
sum+=frac[k]*a[j][k];
}
// sum+=2^k * a[j][k]; sum表示一行//排列的十进制表示数字,hash的
//标标唯一,当下标为不同数时,a的排列也不同。若下标有相同的,则重
//复,即i位数不能表示N个不同排列
if(hash[sum]==0)
hash[sum]=1;
else {ok=0;break; }
}
if(ok&&num<min) { min=num; }
}
printf("%d\n",min);
}
return 0;
}