习题10-1:(洛谷P3392)涂国旗
#include <iostream>
using namespace std;
char a[55][55];
int n, m;
int play(int t,int r) {
int num = 0;
for (int i = 1; i <= t; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] != 'W') num++;
}
}
for (int i = t + 1; i <= r - 1; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] != 'B') num++;
}
}
for (int i = r; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] != 'R') num++;
}
}
return num;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
int min = 2505;
for (int i = 1; i <= n-2; i++) {
for (int j = i + 2; j <= n; j++) {
if (play(i, j) < min) min = play(i, j);
}
}
cout << min;
return 0;
}
通过枚举关键的要素找到突破口,枚举边界线,t为白色最后一行,r为红色第一行。
习题10-2:(洛谷P3654)First Step
#include <iostream>
using namespace std;
char a[220][220];
int main() {
int n, m,z,ans=0,num=0;
cin >> n >> m>>z;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
if (a[i][j] == '.') num++;
}
}
if (z != 1) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m - 1; j++) {
int flag = 1;
for (int c = 0; c <= z - 1; c++) {
if (a[i][j + c] != '.') {
flag = 0;
}
}
if (flag == 1) ans++;
}
}
for (int j = 1; j <= m; j++) {
for (int i = 1; i <= n - 1; i++) {
int flag = 1;
for (int c = 0; c <= z - 1; c++) {
if (a[i + c][j] != '.') {
flag = 0;
}
}
if (flag == 1) ans++;
}
}
}
else {
ans = num;
}
cout << ans;
return 0;
}
需要注意的是,数组不能开110*110,因为100+100在代码中是可能发生的。
习题10-3:(洛谷P1217)回文质数
标答:
#include <iostream>
using namespace std;
bool reflush(int n) {
int a[15];
int x = 0;
while (n) {
a[x++] = n % 10;
n /= 10;
}
int flag = 1;
for (int i = x - 1, j = 0; i >= j; i--, j++) {
if (a[i] != a[j]) {
flag = 0;
break;
}
}
return flag;
}
bool check(int n) {
int sym = 1;
if (n < 2) {
return 0;
}
else {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
sym = 0;
break;
}
}
}
return sym;
}
int main() {
int a, b;
cin >> a >> b;
for (int i = a; i <= b; i++) {
if (i % 2 != 0&&i>=5) {
if (reflush(i) && check(i)) {
cout << i << endl;
}
}
}
return 0;
}
1亿=1*10的8次方,测试点应该不会超过这个数据,因此没有判断,加上判断也可以。
另法:
for (d1 = 1; d1 <= 9; d1+=2) { // 只有奇数才会是素数
for (d2 = 0; d2 <= 9; d2++) {
for (d3 = 0; d3 <= 9; d3++) {
palindrome = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1;//(处理回文数...)
}
}
}
请根据此算法自行编程。
习题10-4:(洛谷P1149)火柴棒等式
#include <iostream>
using namespace std;
//6 2 5 5 4 5 6 3 7 6
typedef struct number {
int num;
int firerod;
}Num;
Num a[10];
int reflush(int n) {
if (n!= 0) {
int m = n;
int b[6] = { 0 };
int x = 0;
int sum = 0;
int num0 = 0;
while (n) {
b[++x] = n % 10;
n /= 10;
sum += a[b[x]].firerod;
}
return sum;
}
else {
return 6;
}
}
int main() {
int n1,ans=0;
cin >> n1;
for (int i = 0; i <= 9; i++) {
a[i].num = i;
switch (i) {
case 0:a[i].firerod = 6; break;
case 1:a[i].firerod = 2; break;
case 2:a[i].firerod = 5; break;
case 3:a[i].firerod = 5; break;
case 4:a[i].firerod = 4; break;
case 5:a[i].firerod = 5; break;
case 6:a[i].firerod = 6; break;
case 7:a[i].firerod = 3; break;
case 8:a[i].firerod = 7; break;
case 9:a[i].firerod = 6; break;
}
}
for (int i = 0; i <=1111; i++) {
for (int j = 0; j <=1111; j++) {
if (reflush(i) + reflush(j) + reflush(i + j) + 4 == n1)
ans++;
}
}
cout << ans;
return 0;
}
经验启示:
1.函数调用后无论如何先保存传入的参数,防止后期该数据了还用改后的当没改前的
2.数组这里没必要开1000,然后用a[m].firerod保存sum,多此一举且占运行时间,导致无法通过。
习题10-5:(洛谷P3799)妖梦拼木棒
#include <iostream>
using namespace std;
#include <algorithm>
int m;
unsigned long long bucket[5005];//计数桶
int main() {
int n;
cin>> n;
int maxn = 0;
unsigned long long sum = 0;
for (int i = 0; i < n; i++) {
cin >> m;
bucket[m]++;
maxn = max(m, maxn);
}
for (int i = 2; i <= maxn; i++) {
if (bucket[i] >= 2) {
for (int c = 1; c <=i/2; c++) {
if (c == i - c) {
sum += bucket[i] * (bucket[i] - 1) / 2 * bucket[c] * (bucket[c] - 1) / 2;
}
else {
sum += bucket[i] * (bucket[i] - 1) / 2 * bucket[c] * bucket[i-c];
}
}
}
}
cout << sum % (1000000000 + 7);
return 0;
}
注意要点:
1.确定数据范围很重要:1<i<=maxn 1<=c<=i-c<i
以此来确定循环的范围,如果c是从1递增到i,那么这个题就会有很多次无效的重复。
2.枚举元素的选择很重要,枚举木棒的长度就是一个优解,而如果用数组保存输入木棒的长度,一方面要开一个很大的数组,另一方面枚举木棒长度不方便简洁。
3.本题思路:
先选两根长度为m的木棒,方案数为组合数c(am,2),am代表长度为m木棒的个数。
再选两根和为m的木棒,(1)i=m-i,方案数为c(am/2,2)
(2)i≠m-i,方案数为c(ai,1)*c(am-i,1)
4.注意不要枚举两个变量i,j,然后判断他们和是否是m,这样会增大运算量,这里单重循环就可以解决问题。m-i不存在那么其方案数表达式就是0不用担心。
习题10-7:(洛谷P2036)Perket
思路:
本题为子集枚举算法。
各个配料被选取为1,否则为0.
从1到全集枚举(从1不从0是因为至少有一个配料被选取),判断单配料是否被选取,如果被选取就更新总酸度和总苦度,通过打擂台的方式确定最小差。不要忘记每次重置总酸度和总苦度。
#include <iostream>
using namespace std;
#include <cmath>
typedef struct element {
int suan;
int ku;
}E;
E a[15];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].suan >> a[i].ku;
}
int multiple_suan = 1;
int sum_ku = 0;
int U = 1 << n;//U-1为全集
long long min = 10000000000;
for (int i = 1; i < U; i++) {
multiple_suan = 1;
sum_ku = 0;
for (int j = 1; j <= n; j++) {
if (1 << j - 1 & i) {
multiple_suan *= a[j].suan;
sum_ku += a[j].ku;
}
}
if (abs(multiple_suan - sum_ku) < min) {
min = abs(multiple_suan - sum_ku);
}
}
cout << min;
return 0;
}
习题10-8:(洛谷P1433)吃奶酪
思路:
本题为排列枚举算法。
设初始的原点为点0,order[0]为0,
之后排列枚举各个点的顺序,从而解决问题。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
typedef struct dot {
double x;
double y;
}D;
D a[20];
int order[20];
double xy(int m,int n) {
return sqrt(pow(a[m].x - a[n].x, 2) + pow(a[m].y - a[n].y, 2));
}
int main() {
int n;
cin >> n;
a[0].x = 0;
a[0].y = 0;
order[0] = 0;
int last = 0;
double sum = 0;
double min = 100000;
for (int i = 1; i <= n; i++) {
cin >> a[i].x >> a[i].y;
order[i] = i;
}
do {
sum = 0;
last = 0;
for (int i = 1; i <= n; i++) {
int j;
for (j = 1; j <= n; j++) {
if (order[j] == i) {
sum += xy(last, j);
break;
}
}
last = j;
}
if (sum < min) min = sum;
} while (next_permutation(order+1,order+n+1));
printf("%.2f", min);
return 0;
}
注意:本算法无误,但是复杂度不够好,只能过50分,现阶段用暴力枚举只能这样了。n<10以内的数据没问题。