目录
枚举,是一种列出所有可能的情况,然后逐个检查是否是问题的解的解题方法;
下面以三道题目来讲解枚举方法;
Packets
大致题意:有6*6的大箱子和1*1 2*2 3*3 4*4 5*5 6*6的木块若干,高度均为h,输入每种箱子的数量,求出最小需要多少个大箱子。
分析:越大的物体越不灵活,从大向小分析。
1.6*6的木块需要一个箱子
2.5*5的木块可以剩下11个1*1木块的空间
3.4*4的木块可以剩下5个2*2的木块的空间
4.3*3的木块四个可占满,所有需要列举四种情况:
5.全部占满正好,空出来一个可以放1个2*2和5个1*1;空出来两个可以放3个2*2和6个1*1;空出来3个可以放5个2*2和7个1*1;
经过分析(a[i]表示i*i的木块个数)可以得出:
3*3需要(a[3]+3)/4个箱子(1-4个3*3需要一个箱子 5-8个需要2个推出需要+3)
那么多出来的2*2的有x=5*a[4]+u[a[3]%4]; u[4]={0,5,3,1};
多出来的1*1的有y=11*a[5]+v[a[3]%4];v[4]={0,7,6,5}
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stdlib.h>
using namespace std;
int main(){
int a[10];
int u[4] = {0,5,3,1};//u,v是针对于3*3能剩下的空间所开的数组 便于计算可装载1*1 2*2的 //个数
int v[4] = {0,7,6,5};
for(;;){
memset(a,0,sizeof(a));
int flag = 0;
for(int i = 1;i <= 6;i++){
cin >> a[i];
if(a[i] != 0) flag = 1;
}
if(flag == 0) break;
int ans = 0;
ans += (a[6]+a[5]+a[4]+(a[3]+3)/4);
int x = 5*a[4]+u[a[3]%4];//2*2;
int y = 11*a[5] + v[a[3]%4];//1*1;
if(x < a[2]){//先判断大的,如果发现2*2有多的 这是就要加上多出来的/9(1个箱 //子可装9个2*2)
int i = (a[2]-x+8)/9;
ans += i;
y += 4*(i*9-(a[2]-x));//多加箱子的话 1*1的也可能会多出来空间装 如 //果2*2装不满的话
}
else y += (x-a[2])*4;//发现2*2比预测可插空的少 那就留给1*1的 不要忘记!!
if(y < a[1]){
ans += (a[1]-y+35)/36; //判断1*1不够插空的话 就加箱子
}
cout << ans << endl;
}
return 0;
}
熄灯问题
大致题意是在5*6的格子里,每个格子里有盏灯,输入初始的亮灭情况,在一个格子里按按钮,那么本身以及上下左右五个灯亮的就熄灭,熄灭的就会变亮,问如何才能全灭,输出是否按按钮的5*6矩阵,按标1,不按标0;
这个题目如何枚举,如果dfs深搜会超时,情况太多。既然只有一种办法能让它全部熄灭,那么不妨枚举第一行的情况,第二行熄灭第一行亮的灯,以此类推,那么必定有一种情况让最后一行灯恰好全灭。
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
int sta[10][10];
int press[10][10];
int ini[10][10];
void reverse_sta(int x,int y){//写下每次按按钮会改变的灯的状况
for(int i = 0;i < 4;i++){
int xx = x+dir[i][0];
int yy = y+dir[i][1];
if(xx < 0||yy < 0||xx > 4||yy > 5) continue;
if(sta[xx][yy] == 1) sta[xx][yy] = 0;
else sta[xx][yy] = 1;
}
if(sta[x][y] == 1) sta[x][y] = 0;//不要忘记本身更改状态
else sta[x][y] = 1;
}
bool check(){//检查最后一行是否恰好全部熄灭
for(int i = 0;i < 6;i++)
if(sta[4][i] == 1 ) return false;
return true;
}
void copy(){
for(int i = 0;i < 5;i++)
for(int j = 0;j < 6;j++)
sta[i][j] = ini[i][j];
}
int main(){
for(int i = 0;i < 5;i++)
for(int j = 0;j < 6;j++)
cin >> ini[i][j];
for(int s = 1 << 5 ; s <= 95;s++){//枚举0000001-1000000 其实与0-64一样 只不过枚举的 //顺序不一样
memset(press,0,sizeof(press));//每次都需要初始化press按钮的按键情况
copy();//重置初始灯状态
for(int i = 0;i < 6;i++){
if((s>>i)&1){//只能枚举第一行按不按 无法枚举亮灭状态 因为无法判断第一行 //怎么按按钮能从初始状态到枚举状态
reverse_sta(0,i);
press[0][i] = 1;
}
}
for(int i = 1;i < 5;i++){
for(int j = 0;j < 6;j++){
if(sta[i-1][j] == 1){
reverse_sta(i,j);
press[i][j] = 1;
}
}
}
if(check()){
for(int i = 0;i < 5;i++){
for(int j = 0;j < 6;j++){
cout << press[i][j] << " ";
}
cout << endl;
}
}
}
return 0;
}
称硬币Counterfeit Dollar
有12枚硬币,代号为A——L,其中有一枚假的,唯一区别就是假币重量和真币不一样,真币重量都是一样的,称量三次,给出了称量方法和结果,问哪一枚是假的,并且输出比真币轻还是重
不是问的称重办法 让你判断哪一枚是假的,那么你就假设这一门是假的,看它符合不符合称重情况。显然,枚举每一个硬币,假设是假的 还要假设重还是轻 满足就输出
代码如下:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
char lefts[3][10];
char rights[3][10];
char results[3][10];
bool exist(char c,char s[3][10],int x){//判断这个字符是否在s数组中
for(int i = 0;i < strlen(s[x]);i++)
if(s[x][i] == c) return true;
return false;
}
bool isfake(char c,int f){//判断c是假币的话出现什么情况应该false 假设都避开了 那就true 也就
//确认是假币了
for(int i = 0;i < 3;i++){ //不可以忽略不在称上的时候的情况
if(f == 1){//轻
switch(results[i][0]){
case 'u': if(!exist(c,rights,i)) return false;
break;
// 如果c不在右边 那么肯定不会天平右边轻-升高,所以false,不可以判断如果在左边就错误,可能没有被
//称重
case 'd': if(!exist(c,lefts,i)) return false;
break;
// 如果c不在左边 那么肯定不会天平右边重-下降,所以false,不可以判断如果在右边就错误,可能没有被
//称重
case 'e':if(exist(c,rights,i) || exist(c,lefts,i)) return false;
break;
//不论c在哪一边 只要是在天平上 如果是假币 就不会出现平衡状态 也就是出现在任何一方 就false
}
}
else if(f == 0){//重
switch(results[i][0]){
case 'u': if(!exist(c,lefts,i)) return false;
break;
//如果c不在左边 那么肯定不会出现右边轻-高的情况,false
case 'd': if(!exist(c,rights,i)) return false;
break;
//如果c不在右边 那么肯定不会出现右边重-低的情况,false
case 'e': if(exist(c,rights,i) || exist(c,lefts,i)) return false;
break;
//不论c在哪一边 只要是在天平上 如果是假币 就不会出现平衡状态 也就是出现在任何一方 就false
}
}
}
return true;
}
int main(){
int n;
cin >> n;
while(n--){
for(int i = 0;i < 3;i++)
cin >> lefts[i] >> rights[i] >> results[i];// up down针对于右边 轻者高
for(char c = 'A';c <= 'L';c++){
if(isfake(c,1)){
printf("%c is the counterfeit coin and it is light.\n",c); break;//注意换行符-输出格式
}
else if(isfake(c,0)){
printf("%c is the counterfeit coin and it is heavy.\n",c);break;
}
}
}
return 0;
}