C语言程序设计100例之(38):涂国旗

例38   涂国旗

题目描述

某国法律规定,只要一个由 N×M 个小方块组成的旗帜符合如下规则,就是合法的国旗。

从最上*干行(至少一行)的格子全部是白色的;

接下来若干行(至少一行)的格子全部是蓝色的;

剩下的行(至少一行)全部是红色的;

现有一个棋盘状的布,分成了 N 行 M 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成该国国旗,方法是在一些格子上涂颜料,盖住之前的颜色。

小a很懒,希望涂最少的格子,使这块布成为一个合法的国旗。

输入格式

第一行是两个整数 N,M。

接下来 N 行是一个矩阵,矩阵的每一个小方块是W(白),B(蓝),R(红)中的一个。

输出格式

一个整数,表示至少需要涂多少块。

输入样例

4 5

WRWRW

BWRWB

WRWRW

RWBWR

输出样例

11

         (1)编程思路。

        定义数组int  w[51],b[51],r[51];数组元素w[i]、b[i]和r[i]分别表示把前i行全部涂成白色、蓝色或红色所需要涂的格子数。

        设蓝色所在的行是第i行到第j行,则第1行到第i-1行是白色,第j+1行到第n行是红色,此时需要涂的格子数为w[i-1]+b[j]-b[i-1]+r[n]-r[j]。

        对蓝色所在的行i~j进行穷举,显然2≤i≤n-1,i≤j≤n-1。对穷举的每种情况找出所需要涂的格子数(w[i-1]+b[j]-b[i-1]+r[n]-r[j])的最小值即可。

         (2)源程序。

#include <stdio.h>

int main()

{

    int w[51]={0},b[51]={0},r[51]={0};

    int n,m;

    scanf("%d%d",&n,&m);

    char s[51];

    int i,j;

    for (i=1;i<=n;i++)

    {

        scanf("%s",s);

        for (j=0;j<m;j++)

        {

            if (s[j]!='W') w[i]++;

            if (s[j]!='B') b[i]++;

            if (s[j]!='R') r[i]++;

        }

        w[i]+=w[i-1];

        b[i]+=b[i-1];

        r[i]+=r[i-1];

    }

    int ans=50*50;

    for (i=2;i<=n-1;i++)

        for (j=i;j<=n-1;j++)

        {

            int t;

            t=w[i-1]+b[j]-b[i-1]+r[n]-r[j];

           if (ans>t) ans=t;

        }

    printf("%d\n",ans);

         return 0;

}

习题38

38-1  健康的奶牛

        本题选自洛谷题库 (https://www.luogu.org/problem/ P1460)。

题目描述

农民 John 以拥有世界上最健康的奶牛为傲。他知道每种饲料中所包含的牛所需的最低的维他命量是多少。请你帮助农夫喂养他的牛,以保持它们的健康,使喂给牛的饲料的种数最少。

给出牛所需的最低的维他命量,输出喂给牛需要哪些种类的饲料,且所需的饲料剂量最少。

维他命量以整数表示,每种饲料最多只能对牛使用一次,数据保证存在解。

输入格式

第一行一个整数 v(1≤v≤25),表示需要的维他命的种类数。

第二行 v 个整数,表示牛每天需要的每种维他命的最小量。

第三行一个整数 g(1≤g≤15),表示可用来喂牛的饲料的种数。

下面 g 行,第 n 行表示编号为 n 饲料包含的各种维他命的量的多少。

输出格式

输出文件只有一行,包括牛必需的最小的饲料种数 p;后面有 p 个数,表示所选择的饲料编号(按从小到大排列)。

如果有多个解,输出饲料序号最小的(即字典序最小)。

输入样例

4

100 200 300 400

3

50  50  50  50

200 300 200 300

900 150 389 399

输出样例

2 1 3

         (1)编程思路。

        g种饲料,每种有选用和不选用两种状态。一共有2g种状态,因为g最多为15,因此状态量最多为215,可以用二进制位运算枚举g种饲料的各种状态组合,检查每种状态组合是否满足牛所需的最低的维他命量,并进行相应的更新处理。

         例如,当g=3时,有3种饲料(设为a,b,c这3种),8种组合状态,其中0(000)表示三种饲料均不选用,1(001)表示只选用饲料c,2(010)表示只选用饲料b,3(011)表示选用饲料b和c,4(100)表示选用饲料a,…,7(111)表示3种饲料全部选用。

        (2)源程序。

#include <stdio.h>

int main()

{

    int v,g;

    scanf("%d",&v);

    int a[26];

    int i,j,k;

    for (i=0;i<v;i++)

       scanf("%d",&a[i]);

    scanf("%d",&g);

    int map[16][26];

    for (i=0;i<g;i++)

      for (j=0;j<v;j++)

         scanf("%d",&map[i][j]);

    int cnt=15,num;

    for (k=1;k<(1<<g);k++)      //  枚举所有状态

    {

       int lowbit=k;

       int b[26];

       for (i=0;i<v;i++)

           b[i]=0;

       int p=0;

       for (i=0; lowbit>0; i++,lowbit>>=1)

          if (lowbit&1)

          {

              p++;

              for (j=0;j<v;j++)

              {

                  b[j]+=map[i][j];

              }

           }

       for (i=0;i<v;i++)

            if (a[i]>b[i]) break;

       if (i>=v && p<cnt)

       {

          cnt=p;

          num=k;

       }

    }

    printf("%d ",cnt);

    for (i=1; num>0;i++,num>>=1)

       if (num&1)

          printf("%d ",i);

    printf("\n");

    return 0;

}

38-2  海明码

        本题选自洛谷题库 (https://www.luogu.org/problem/ P1461)。

题目描述

给出 n、b、d,要求找出n个由 0、1组成的编码,每个编码有b位,使得两两编码之间至少有 d个单位的 “Hamming距离”。

“Hamming距离”是指对于两个编码,它们二进制表示法中的不同二进制位的数目。看下面的两个编码 0x554 和 0x234(十六进制数)

0x554 = 0101 0101 0100

0x234 = 0010 0011 0100

不同位     xxx    xx

因为有五个位不同,所以“Hamming距离”是 5。

输入格式

一行,包括 n,b,d。1≤n≤64,1≤b≤8,1≤d≤7。

输出格式

n 个编码(用十进制表示),要排序,十个一行。

如果有多解,你的程序要输出这样的解:假如把它化为 2b进制数,它的值要最小。

输入样例

16 7 3

输出样例

0 7 25 30 42 45 51 52 75 76

82 85 97 102 120 127

         (1)编程思路。

        由于二进制数位的异或运算具有一个特点:相同的数位异或为0,不同的数位异或为1。因此,两个数的“Hamming距离”实际就是两个数异或后,结果中二进制数位“1”的个数。

定义数组int a[256];存放满足“Hamming距离”大于或等于d的数。初始时a[0]=0,表示0是保存的第1个数。

        用循环对1~2b-1之间的每个整数进行穷举,将穷举到的当前数i和a数组中已经保存的所有数据进行比较,若i分别与已保存的每个数异或的结果中“1”的个数均不小于d,则保存数据i。

      (2)源程序。

#include <stdio.h>

int bitCount(int x)

{

    int cnt=0;

    while (x!=0)

    {

        cnt++;

        x=x&(x-1);

    }

    return cnt;

}

int main(void)

{

    int n,b,d;

    scanf("%d%d%d",&n,&b,&d);

    int a[256];

    int cnt=1;

    a[0]=0;

    int i,j;

    for (i=1;i<(1<<b);i++)

    {

        for (j=0;j<cnt;j++)

            if (bitCount(i^a[j])<d) break;

        if(j>=cnt)

            a[cnt++] = i;

        if (cnt == n)

            break;

    }

    for (i=0;i<cnt;i++)

    {

        printf("%d ",a[i]);

        if ((i+1)%10==0)

            printf("\n");

    }

    return 0;

}

38-3  彩弹游戏

        本题选自洛谷题库(https://www.luogu.org/problem/ P6200)。

题目描述

奶牛们最近从玩具商那里,买来了一套仿真版彩弹游戏设备(类似于真人 CS)。Bessie 把她们玩游戏的草坪划分成了 N×N 的矩阵(1≤N≤100),同时她算出了她的 K 个对手在草地上的位置(1≤K≤105),现在你需要帮 Bessie 算些东西。

在这个游戏中,奶牛们用一把枪向八个方向中的任意一个方向射出子弹,这八个方向分别是:正北,正南,正东,正西,东北,东南,西北,西南(东北指北偏东45∘,东南,西北,西南同理)。

Bessie 想要你算出,有多少个位置可以让她射到所有对手。特别地,Bessie 可以和她的某一个对手站在同一格子,这时候她可以射到和她同一格子的对手。

输入格式

第一行两个整数 N,K。

接下来 K 行,每行两个整数 Ri和 Ci,表示第 i 头奶牛在第Ri行第Ci列。可能有两个奶牛在同一位置上。

输出格式

输出 Bessie 可以选择的格子数目。

输入样例

4 3

2 1

2 3

4 1

输出样例

5

        (1)编程思路。

        用二重循环对(1,1)~(N,N)这N2个位置进行穷举,若某个位置能射到所有对手,则计数。

       (2)源程序。

#include <stdio.h>

int abs(int a)

{

    return a>=0?a:-a;

}

int main()

{

    int n,k;

    scanf("%d%d",&n,&k);

    int i,x,y;

    int tot=0,ans=0;

    int v[101][101]={0};

    int a[10001][2];

    for (i=1;i<=k;i++)

    {

        scanf("%d%d",&x,&y);

        if (!v[x][y])

        {

           v[x][y]=1;

           a[tot][0]=x;

           a[tot][1]=y;

           tot++;

        }

    }

    for (x=1;x<=n;x++)

      for (y=1;y<=n;y++)

      {

          int f=1;

          for (i=0;i<tot;i++)

          {

             int dx=abs(x-a[i][0]);

             int dy=abs(y-a[i][1]);

             if (dx!=dy && dx!=0 && dy!=0)

             {

                 f=0;

                 break;

             }

          }

          ans+=f;

        }

    printf("%d\n",ans);

    return 0;

}

上一篇:Beat the Spread![HDU1194]


下一篇:大规模天线技术的研究方向 |带你读《大规模天线波束赋形 技术原理与设计 》之九