例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;
}