程设作业之枚举

一.画家问题

有一个正方形的墙,由N*N个正方形的砖组成,其中一些砖是白色的,另外一些砖是黄色的。Bob是个画家,想把全部的砖都涂成黄色。但他的画笔不好使。当他用画笔涂画第(i, j)个位置的砖时, 位置(i-1, j)、 (i+1, j)、 (i, j-1)、 (i, j+1)上的砖都会改变颜色。请你帮助Bob计算出最少需要涂画多少块砖,才能使所有砖的颜色都变成黄

输入第一行是一个整数n (1≤n ≤15),表示墙的大小。接下来的n行表示墙的初始状态。每一行包含n个字符。第i行的第j个字符表示位于位置(i,j)上的砖的颜色。“w”表示白砖,“y”表示黄砖。输出一行,如果Bob能够将所有的砖都涂成黄色,则输出最少需要涂画的砖数,否则输出“inf”。

样例输入:

  5

  wwwww

  wwwww

  wwwww

  wwwww

  wwwww

代码如下:

  

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <cstdio>
#include <algorithm>
using namespace std;

int wall[20][20]={0};   //存储原始状态的函数 
int press[20][20]={0};//存储处理方法的函数 
int n=0,minn=9999,count0=0;//n为规模,minn为最小处理数 
bool flag =0;   //该变量表示问题是否有解
 
void init(){    //初始化函数,涂为1,未涂为0; 这个函数没有问题 
    cin>>n;
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            char t;
            cin >> t;
            if(t=='w') wall[i][j]=0;
            else wall[i][j]=1;
        }
    }
}

bool guess(){
    for (int i=1;i<n;i++){
        for (int j=1;j<=n;j++){
            press[i+1][j]=(wall[i][j] + press[i][j] + press[i][j - 1] + press[i][j + 1] + press[i - 1][j]+1) % 2;//除了第一行,其余每一行都可以根据上一行唯一确定其涂色方式;这一行就是确定解的方程
        }
    }
    for (int j=1;j<=n;j++){
            if((press[n][j - 1] + press[n][j + 1] + press[n-1][j]+press[n][j]) % 2 == wall[n][j])  //每一行都是根据上一行确定的,所以我们可以根据最后一行是否满足要求来判断问题是否有解 
            return false;
        }
        return true;        
}

void solve() {
    while (true)
    {
        if (guess()) {
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                {
                    if (press[i][j]==1)count0++;
                }
            minn = min(count0, minn);
            count0 = 0;
            flag = 1;
        }
        for (int i = 2; i <= n; i++)
            for (int j = 1; j <= n; j++)
                press[i][j] = 0;
        press[1][1]++;  //对第一行进行枚举
        int c = 1;
        while (press[1][c] > 1) {    
            press[1][c] = 0;
            press[1][++c]++;
        }
        if (c == n + 1)
            break;
    }
}

int main(){
    init();
    solve();
    if(flag==1)  
    cout << minn << endl;
    else
    cout << "inf" << endl;
    return 0;
} 
View Code

二.拨钟问题

  

有9个时钟,排成一个3*3的矩阵。

|-------|    |-------|    |-------|
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C

|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F

|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I
(图 1)

现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。


移动    影响的时钟

1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI

 

输入9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。输出输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。样例输入

3 3 0 
2 2 2 
2 1 2 

样例输出

4 5 8 9 

代码如下:
#include<cstdio>
using namespace std;
//同上一题,我们发现除了第一行需要枚举外,第二行第三行中的每个格子基本都可以通过已知条件得出。
int z[10],i[10],sum;
int main()
{
    for(int j=1;j<=9;j++)scanf("%d",&z[j]);
    for(i[1]=0;i[1]<4;i[1]++)
        for(i[2]=0;i[2]<4;i[2]++)
            for(i[3]=0;i[3]<4;i[3]++)
            {
                i[4]=(4-(z[1]+i[1]+i[2])%4)%4;
                i[5]=(4-(z[2]+i[1]+i[2]+i[3])%4)%4;
                i[6]=(4-(z[3]+i[2]+i[3])%4)%4;
                i[7]=(4-(z[4]+i[1]+i[4]+i[5])%4)%4;
                i[9]=(4-(z[6]+i[3]+i[5]+i[6])%4)%4; 
                i[8]=(4-(z[8]+i[5]+i[7]+i[9])%4)%4;//这里要注意第8个格子是由第九个格子决定的
                sum=0;//判断是否可解
                sum+=(z[1]+i[1]+i[2]+i[4])%4;
                sum+=(z[2]+i[1]+i[2]+i[3]+i[5])%4;
                sum+=(z[3]+i[2]+i[3]+i[6])%4;
                sum+=(z[4]+i[1]+i[4]+i[5]+i[7])%4;
                sum+=(z[5]+i[1]+i[3]+i[5]+i[7]+i[9])%4;
                sum+=(z[6]+i[3]+i[5]+i[6]+i[9])%4;
                sum+=(z[7]+i[4]+i[7]+i[8])%4;
                sum+=(z[8]+i[5]+i[7]+i[8]+i[9])%4;
                sum+=(z[9]+i[6]+i[8]+i[9])%4;
                if(sum==0){for(int j=1;j<=9;j++)while(i[j]--)printf("%d ",j);return 0;}
            }
}
View Code

三.特殊密码锁

  描述

有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。

然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。

当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。

输入两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。输出至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。样例输入

011
000

样例输出

1
代码如下:
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <cstdio>
#include <algorithm>
#include <string>
#include <memory.h>
using namespace std;
//该题较简单,只需枚举第一个格子是否需要改变(两种情况),比较两种情况的大小即可
const int maxn = 35;
int key[maxn], pwd[maxn], press[maxn], aft[maxn], leng, success = 0, step = 0, minstep = 9999;//press-whether to change the status;aft-status after the press

void init() {
    string s1, s2;
    cin >> s1 >> s2;
    int l1 = s1.length();
    for (int i = 0; i < l1; i++) {
        key[i + 1] = s1[i] - '0';
        pwd[i + 1] = s2[i] - '0';
        aft[i + 1] = s1[i] - '0';
    }
    leng = l1;
}
void solve() {
    for (int op = 0; op <= 1; op++) {
        memset(press, 0, sizeof(int)*maxn);
        step = 0;
        press[1] = op; aft[1] ^= op; aft[2] ^= op;
        step += op;
        for (int i = 2; i <= leng; i++) {
            if (aft[i - 1] != pwd[i - 1]) {
                press[i] = 1; step++;
            }
            aft[i] ^= press[i];
            aft[i-1] ^= press[i];
            aft[i+1] ^= press[i];
            if (i == leng && aft[i] == pwd[i]) {
                success = 1;
                minstep = min(step, minstep);
            }
        }
        for (int i = 1; i <= leng; i++)aft[i] = key[i];
    }
    if (success)printf("%d\n", minstep);
    else printf("impossible\n");
}

int main() {
    init();
    solve();
    return 0;
}
View Code

期末苟住!!!

猜你喜欢

转载自www.cnblogs.com/shukai2000/p/10986782.html