When you were a little kid, you would stamp papers randomly but your parents were still so impressed by your art. Now you are dealing with the UCF Programming Team Coaches and you need to be more creative to impress them. Good luck!
You are given a star shaped stamp like the one below. The black area is covered in ink and the white area is not. When the stamp hits the paper, it leaves a mark for each cell of ink that hits the paper.
For example, the image below can be made with two stampings. Notice the stamp must always remain axis-aligned when hitting the paper. We also require that the stamp be completely contained within the paper. Note a cell of paper stamped once with black ink is indistinguishable from a cell of paper stamped multiple times with black ink. Note also that cells and stamp line up properly, i.e., a cell is either covered completely by the stamp or not covered at all, i.e., the stamp will not cover part of a cell.
The Problem:
Given a black and white image, determine the minimum number of times, if possible, you would need to stamp the paper with the star stamp to end up with the design specified.
The Input:
The first input line contains a positive integer, n, indicating the number of images to evaluate. Each image starts with a line containing two integers, rr and cc , (1≤r≤9,1≤c≤9), representing the number of rows and columns, respectively. The next rr input lines contains cc characters each. The characters are either ‘.’, representing a blank cell of the image and ‘#’,representing a cell of the image covered in ink.
The Output:
For each image, output “Image #d: v” where v is the minimum number of stampings required to make the image. Replace v with “impossible” (without quotes) if it is not possible to form the image using the star shaped stamp. Leave a blank line after the output for each test case.
样例输入
5
1 1
.
1 1
#
3 3
.#.
###
.#.
3 5
.#.#.
#####
.#.#.
4 7
.##.#..
######.
.######
..#..#.
样例输出
Image #1: 0
Image #2: impossible
Image #3: 1
Image #4: 2
Image #5: 5
贪心题,贪心决策是选择当前最优方案,然而结果可能与最优方案的选择的顺序有关
,比如说这个样例
1
8 7
.#.##..
######.
#####..
.#.###.
.#####.
#####..
.##....
.......
最优解是8,但是如果仅仅每一步选择最优方案可能得到的结果是9
因此如果最优方案与顺序有关,需要枚举选择的顺序。因此这也是一道搜索题。
在dfs前先将纸张的周围一圈遍历一下,先将边缘待染色的格子染色。因为对于边缘的某一个待染色的格子,至多有一种方案使其染色,并且迟早要选择这种方案使其染色,因此与顺序无关。通过这个处理,使得搜索范围大大的减少。(必须优化,否则TLE)
dfs时要优先选择当前最优的方案,即待染色的区域中未染色的区域多的优先。如果多个方案均为最优,则选定其中一个方案,判断是否存在其他最优方案满足染色区域与选定方案的染色区域的相交且相交的地方未染色,如果不存在其他最优方案满足,则选定区域与其它最优方案互不影响,即选定的方案不会影响其它最优方案的选择与顺序,选择选定方案继续搜索,否则分别选择选定的方案和与选定的方案有影响的方案进行搜索。(必须优化,否则TLE)
另外在dfs时果当前操作数加上未染色的方格数除5向上取整的结果大于等于当前最优操作数则直接return。(可以进一步提高效率)
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N = 15;
int n, m, ans;
char g[N][N], gk[N * N][N][N];
int cnt;
bool vis[N][N];
pii used;
int dx[] = {0, 1, 0, -1, 0};
int dy[] = {0, 0, 1, 0, -1};
inline int count(int x, int y) {
int res = 0;
for (int i = 0; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (g[nx][ny] == '#')res++;
else if (g[nx][ny] == '.')return -1;
}
return res;
}
inline void change(int x, int y) {
for (int i = 0; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
g[nx][ny] = '*';
}
}
inline bool is_vaild(int x, int y) {
for (int i = 1; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m)return false;
}
return true;
}
inline bool check(int x, int y) {
if (abs(x - used.fi) + abs(y - used.se) > 2)return false;
if (abs(x - used.fi) + abs(y - used.se) == 1)
return g[x][y] == '#' || g[used.fi][used.se] == '#';
if (abs(x - used.fi) == 2) return g[(x + used.fi) / 2][y] == '#';
if (abs(y - used.se) == 2) return g[x][(y + used.se) / 2] == '#';
return g[x][used.se] == '#' || g[used.fi][y] == '#';
}
inline void dfs(int u, int res) {
if (!res) {
ans = u;
return;
}
if (u + (res + 4) / 5 >= ans)return;
int mnum = 0;
vector<pii > ch;
used = {100, 100};
for (int i = 2; i <= n - 1; i++)
for (int j = 2; j <= m - 1; j++)
if (!vis[i][j]) {
int num = count(i, j);
if (num > mnum) {
mnum = num;
ch.clear();
ch.emplace_back(i, j);
used = {i, j};
} else if (num == mnum && check(i, j)) {
ch.emplace_back(i, j);
}
}
if (ch.empty())return;
memcpy(gk[u], g, sizeof(g));
for (auto &it:ch) {
change(it.fi, it.se);
vis[it.fi][it.se] = true;
dfs(u + 1, res - mnum);
vis[it.fi][it.se] = false;
memcpy(g, gk[u], sizeof(g));
}
}
int main() {
int T;
cin >> T;
for (int t = 1; t <= T; t++) {
memset(vis, false, sizeof(vis));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%s", g[i] + 1);
cnt = 0;
ans = 1000;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (g[i][j] == '#')cnt++;
printf("Image #%d: ", t);
bool flag = true;
int tc = 0;
for (int i = 1; i <= n; i++) {
if (g[i][1] == '#') {
if (!is_vaild(i, 2)) {
flag = false;
break;
} else {
int num = count(i, 2);
if (num == -1) {
flag = false;
break;
} else change(i, 2), cnt -= num, tc++;
}
}
if (g[i][m] == '#') {
if (!is_vaild(i, m - 1)) {
flag = false;
break;
} else {
int num = count(i, m - 1);
if (num == -1) {
flag = false;
break;
} else change(i, m - 1), cnt -= num, tc++;
}
}
}
if (flag)
for (int j = 1; j <= m; j++) {
if (g[1][j] == '#') {
if (!is_vaild(2, j)) {
flag = false;
break;
} else {
int num = count(2, j);
if (num == -1) {
flag = false;
break;
} else change(2, j), cnt -= num, tc++;
}
}
if (g[n][j] == '#') {
if (!is_vaild(n - 1, j)) {
flag = false;
break;
} else {
int num = count(n - 1, j);
if (num == -1) {
flag = false;
break;
} else change(n - 1, j), cnt -= num, tc++;
}
}
}
if (flag)dfs(0, cnt);
if (ans == 1000)puts("impossible");
else printf("%d\n", tc + ans);
puts("");
}
return 0;
}
虽然上面的做法能AC但是实际上这种做法是错误的,比如这个数据
1
9 9
.#..#..#.
#########
.#######.
.#######.
####.####
.#######.
.#######.
#########
.#..#..#.
正确答案是15,但是上述方法算的的结果是16。
错误的原因在于贪心策略错误,因为最优决策不一定是选择涂色最多的方案,所以,直接枚举可涂色的位置涂色与不涂色的情况,才能将所有情况考虑在内。
#include<bits/stdc++.h>
using namespace std;
const int N = 15;
int n, m, ans;
char g[N][N], gk[N * N][N][N];
int cnt;
bool vis[N][N];
int dx[] = {0, 1, 0, -1, 0};
int dy[] = {0, 0, 1, 0, -1};
inline int count(int x, int y) {
int res = 0;
for (int i = 0; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (g[nx][ny] == '#')res++;
else if (g[nx][ny] == '.')return -1;
}
return res;
}
inline void change(int x, int y) {
for (int i = 0; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
g[nx][ny] = '*';
}
}
inline bool is_vaild(int x, int y) {
for (int i = 1; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m)return false;
}
return true;
}
void dfs(int x, int y, int u, int res) {
if (!res) {
ans = min(ans, u);
return;
}
if (u + (res + 4) / 5 >= ans)return;
if (x <= 2 || x >= n - 1 || y <= 2 || y >= m - 1)return;
int num = count(x, y);
if (num > 0) {
memcpy(gk[u], g, sizeof(g));
change(x, y);
if (y + 1 >= m - 1)dfs(x + 1, 3, u + 1, res - num);
else dfs(x, y + 1, u + 1, res - num);
memcpy(g, gk[u], sizeof(g));
}
if (y + 1 >= m - 1)dfs(x + 1, 3, u, res);
else dfs(x, y + 1, u, res);
}
int main() {
int T;
cin >> T;
for (int t = 1; t <= T; t++) {
memset(vis, false, sizeof(vis));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%s", g[i] + 1);
cnt = 0;
ans = 1000;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (g[i][j] == '#')cnt++;
printf("Image #%d: ", t);
bool flag = true;
int tc = 0;
for (int i = 1; i <= n; i++) {
if (g[i][1] == '#') {
if (!is_vaild(i, 2)) {
flag = false;
break;
} else {
int num = count(i, 2);
if (num == -1) {
flag = false;
break;
} else change(i, 2), cnt -= num, tc++;
}
}
if (g[i][m] == '#') {
if (!is_vaild(i, m - 1)) {
flag = false;
break;
} else {
int num = count(i, m - 1);
if (num == -1) {
flag = false;
break;
} else change(i, m - 1), cnt -= num, tc++;
}
}
}
if (flag)
for (int j = 1; j <= m; j++) {
if (g[1][j] == '#') {
if (!is_vaild(2, j)) {
flag = false;
break;
} else {
int num = count(2, j);
if (num == -1) {
flag = false;
break;
} else change(2, j), cnt -= num, tc++;
}
}
if (g[n][j] == '#') {
if (!is_vaild(n - 1, j)) {
flag = false;
break;
} else {
int num = count(n - 1, j);
if (num == -1) {
flag = false;
break;
} else change(n - 1, j), cnt -= num, tc++;
}
}
}
if (flag)dfs(3, 3, 0, cnt);
if (ans == 1000)puts("impossible");
else printf("%d\n", tc + ans);
puts("");
}
return 0;
}
对拍自取
#include <bits/stdc++.h>
using namespace std;
long long random(int n) {
return 1ll * rand() * rand() % n;
}
char a[20][20], b[20][20];
int dx[] = {0, 1, 0, -1, 0};
int dy[] = {0, 0, 1, 0, -1};
inline void print(int n, int m) {
int x = random(abs(n - 2)) + 1, y = random(abs(m - 2)) + 1;
for (int i = 0; i < 5; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m)
b[nx][ny] = '#';
}
}
int main() {
srand((unsigned) time(0));
int T = 10;
printf("%d\n", T);
for (int i = 0; i <= 12; i++)
for (int j = 0; j <= 12; j++)
a[i][j] = '.';
while (T--) {
int n = random(7) + 3, m = random(7) + 3;
printf("%d %d\n", n, m);
memcpy(b, a, sizeof(b));
int t = random(10) + 2;
while (t--)print(n, m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
putchar(b[i][j]);
puts("");
}
}
return 0;
}