算法笔记 - 枚举

第八章 枚举

8.1 按钮开关问题 (若局部被确定则整体状态被确定)

特殊密码锁 枚举第一个按钮的状态(这里第一个按钮作为局部,一旦确定就可推出全部按钮状态)
这里写图片描述

#include<memory>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 32 
using namespace std;
void SetBit(char & c)
{
    if(c == '0')    c = '1';
    else            c = '0';
} 

void Change(char switchs[],int i,int length)
{
    if( i-1 >= 0   )    SetBit(switchs[i-1]);
    SetBit(switchs[i]);
    if( i+1 < length)   SetBit(switchs[i+1]);
}
int main()
{
    char oriswitchs[N];
    char switchs[N];
    char aim[N];
    int num1 = 0,num2 = 0,length = 0;
    int min_num=0;
    bool tag = false;
    cin >> oriswitchs >> aim;
    length = strlen(oriswitchs);
    //枚举首位操作状态(注意不是首位状态)
    for(int k = 0 ; k < 2 ; ++k)
    {     
        //每次都要重新初始化当前状态  
        memcpy(switchs,oriswitchs,sizeof(oriswitchs));
        if(k)//首位不翻转 
        {
            for(int i = 1; i < length ; ++i )
            {
                //确定首位操作后,之后的每一位都依据前一位状态决定当前操作
                //可以换成先更新当前位状态temp[i]再更新下一位的操作状态button[i+1]
                if(switchs[i-1] != aim[i-1])
                {
                    Change(switchs,i,length);
                    ++num1;
                }
            }
            if(switchs[length-1] == aim[length-1])  tag=true;
        }
        else//首位翻转 
        {
            Change(switchs,0,length);
            ++num2;
            //确定首位操作后,之后的每一位都依据前一位状态决定当前操作
            for(int j = 1; j < length ; ++j )
            {
                if(switchs[j-1] != aim[j-1])
                {
                    Change(switchs,j,length);
                    ++num2;
                }
            }
            if(switchs[length-1] == aim[length-1])  tag=true;
        }       
    }
    if(tag)
    {
        min_num = min(num1,num2);
        cout << min_num;
    } 
    else    
        cout << "impossible";
    return 0;
    }

2811 熄灯问题 (等于二维的密码锁,这里一个局部是第一行的操作状态,通过枚举第一行的操作状态得到所有灯的操作) 输出方案
这里写图片描述
这里写图片描述

//定义 bool 灯状态[][];bool 灯按钮状态[][];bool 临时灯状态[][];
//输入 灯状态
//枚举第一行灯按钮状态:
//               初始化临时灯状态
//               遍历每一行临时灯状态:
//                               更新该行临时灯状态
//                               更新下行临时灯操作状态
//               检查最后一行临时灯状态
//               goto:结束
#include <iostream>
#include <cstring>
using namespace std;
bool light[6][7];
bool button[6][7];
bool temp[6][7];
void change(int i,int j)
{
    temp[i][j] = !temp[i][j];
    temp[i-1][j] = !temp[i-1][j];
    temp[i+1][j] = !temp[i+1][j];
    temp[i][j-1] = !temp[i][j-1];
    temp[i][j+1] = !temp[i][j+1];
}
bool press()
{
    //每次枚举之后的检查都要重新变为初始状态
    memcpy(temp,light,sizeof(temp));
    for (int i=1;i<=5;i++)
    {
        //更改该行的灯状态
        for (int j=1;j<=6;j++)
            if (button[i][j])
                change(i,j);
        //更改下行的灯操作状态
        for (int j=1;j<=6;j++)
            if (temp[i][j]) button[i+1][j] = 1;
    }
    //最后一行是否全亮
    for (int i=1;i<=6;i++)
        if (temp[5][i])
            return false;
    return true;
}
int main()
{
        //输入灯状态
        for (int m=1;m<=5;m++)
            for (int n=1;n<=6;n++)
                cin>>light[m][n];
        //枚举灯操作状态(注意枚举的是灯操作不是灯状态)
        for (int a=0;a<=1;a++)
            for (int b=0;b<=1;b++)
                for (int c=0;c<=1;c++)
                    for (int d=0;d<=1;d++)
                        for (int e=0;e<=1;e++)
                            for (int f=0;f<=1;f++)
                            {
                                memset(button,0,sizeof(button));
                                button[1][1] = a;
                                button[1][2] = b;
                                button[1][3] = c;
                                button[1][4] = d;
                                button[1][5] = e;
                                button[1][6] = f;
                                if(press())
                                    goto L;
                            }
L:
        for (int i=1;i<=5;i++)
        {
            for (int j=1;j<=6;j++)
                cout<<button[i][j]<<" ";
            cout<<endl;
        }
    }
    return 0;
}

2813 画家问题 (同熄灯问题) 输出所有方案中按键次数最小的那个
这里写图片描述
这里写图片描述

//熄灯问题中找到第一个可行方案就直接退出,这里更改为"更新" 
if(guess())
    min_step=min(min_step,step);
    //step在每一次枚举开始前都要初始化为0遇到button[i]==1则+1;

8.2 多层枚举

2747 数字方格
这里写图片描述

 #include<cstdio>
    #include<math.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<string>
    #include<map>
    #include<set>
    #include<vector>
    #define MAX 210
    #define  num 32767
    #define INF 0x7f7f7f
    #define eps 1e-5
    using namespace std;
    int n,ans=0;
    int main()
    {
       //freopen("input.txt","r",stdin);
       cin>>n;
       //三层for循环,从大到小枚举,每层都要由条件剪枝
       for(int i=n;i>=0;i--)
       {
           for(int j=n;j>=0;j--)
           {
               if((i+j)%2!=0) continue;
               for(int k=n;k>=0;k--)
               {
                   if((j+k)%3!=0) continue;
                   if((i+k+j)%5==0)
                       ans=max(ans,(i+j+k));
               }
           }
       }
       cout<<ans;
       return 0;
    }

2977 生理周期
这里写图片描述

//使用三个循环,每一个都是对同一个变量不同周期循环(不是三层)
for(num =开始天+1;num<最大天;num++)
    if((num-第一次体力天)%体力周期==0)break;
for(;num<最大天;num+=体力周期)
    if((num-第一次情感天)%情感周期==0)break;
for(;num<最大天;num+=(体力周期*情感周期))
    if((num-第一次智力天)%智力周期==0)break;
//num为终所求

2814 完美立方
这里写图片描述

//使用四层循环依次枚举abcd
for(a=6;a<n;a++)//a
{
    for(b=2;b<a-1;b++)//b
    {
        if(cube[a]<cube[b]+cube[b]+cube[b]) break;//剪枝
        for(c=b;c<a;c++)//c
        {
            if(cube[a]<cube[b]+cube[c]+cube[c]) break;//剪枝
            for(d=c;d<a;d++)//d
            {
                if(cube[a]==cube[b]+cube[c]+cube[d]) 输出;
            }
        }
    }
}

2814 拨钟问题
这里写图片描述
这里写图片描述


#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
 int station[9] = {0};
 for (int i = 0; i < 9; i++)
  cin>>station[i];
 int move_count[9] = {0};
 int min_count = 10000;
 for (int i1 = 0; i1 < 4; i1++)
  for (int i2 = 0; i2 < 4; i2++)
   for (int i3 = 0; i3 < 4; i3++)
    for (int i4 = 0; i4 < 4; i4++)
     for (int i5 = 0; i5 < 4; i5++)
      for (int i6 = 0; i6 < 4; i6++)
       for (int i7 = 0; i7 < 4; i7++)
        for (int i8 = 0; i8 < 4; i8++)
         for (int i9 = 0; i9 < 4; i9++)
         {
          if ((0 == (i1 + i2 + i4 + station[0]) % 4) && (0 == (i1 + i2 + i3 + i5 + station[1])
              % 4) && (0 == (i2 + i3 + i6 + station[2]) % 4) && (0 == (i1 + i4 + i5 + i7 +
              station[3]) % 4) && (0 == (i1 + i3 + i5 + i7 + i9 + station[4]) % 4) && (0 ==
              (i3 + i5 + i6 + i9 + station[5])% 4) && (0 == (i4 + i7 + i8 + station[6]) % 4)
          && (0 == (i5 + i7 + i8 + i9 + station[7])% 4) && (0 == (i6 + i8 + i9 + station[8]) % 4))
          {
           int sum = i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9;
           if (min_count > sum)
           {
            min_count = sum;
            move_count[0] = i1;
            move_count[1] = i2;
            move_count[2] = i3;
            move_count[3] = i4;
            move_count[4] = i5;
            move_count[5] = i6;
            move_count[6] = i7;
            move_count[7] = i8;
            move_count[8] = i9;
           }
          }
         }
 int cur = 0;
 for (cur = 0; cur < 9; cur++)
  while (move_count[cur]--)
   cout<<cur + 1<<" ";
 cout<<endl;
 return 0;
}

8.3 数学问题先转换后枚举
2814 拨钟问题
这里写图片描述
这里写图片描述

//bc-1 = ab+ac
//因为b和c一定大于a
//故令b=a+m,c=a+n;
//则mn=a^2+1;
//b+c = 2a+m+(a^2+1)/m 枚举m算最小值
#include<stdio.h> 
typedef long long ll;
int main()
{
    ll a,m,ans;
    while(scanf("%lld",&a)!=EOF)
    {
        for(m=a;m>=1;m--)
            if((a*a+1)%m==0)break;
        ans=(a*a+1)/m+m+2*a;//b+c的值 
        printf("%lld\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值