(一道好题)[EOJ][352][Hypnotic Milk Improvement][搜索]
Posted on 2011-03-15 12:52 ch_g 阅读(224) 评论(0) 编辑 收藏 举报解题报告是网上搜到的,在代码的下面(是英文的)
/*
题目:Hypnotic Milk Improvement
题目来源:EOJ 352http://202.120.106.94/onlinejudge/problemshow.php?pro_id=352题目难度:中等偏难
题目内容或思路:
搜索
一道不错的题目,一开始我是乱搞过的
给一个6*6的矩阵,矩阵中每个数的范围是0~9,可以对矩阵进行的操作
有某一行的数加一、某一列的数加一或主对角线上的数加一;其中9加上1变为0。现求不限操作次数,矩阵内所有元素和最大为多少
做题日期:2011.3.15
*/
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <bitset>
#include <cmath>
#include <set>
#include <utility>
#include <ctime>
#define sqr(x) ((x)*(x))
using namespace std;
int tab[6][6], res, row[6];
int stat[1000000];
int ten[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
void init() {
memset(stat, -1, sizeof(stat));
int t[6], st[10];
memset(t, 0, sizeof(t));
for (int i = 0; i < 100000; ++i) {
int p = 0;
while (t[p] == 9) t[p++] = 0;
if (i) t[p]++;
int best = 0, cnt, s = i;
for (int add = 0; add < 10; ++add) {
cnt = 0;
for (int j = 0; j < 6; ++j) {
t[j]++;
s += ten[j];
if (t[j] > 9){
t[j] -= 10;
s -= ten[j + 1];
}
cnt += t[j];
}
best = max(best, cnt);
st[add] = s;
}
for (int j = 0; j < 10; ++j)
stat[st[j]] = best;
}
}
void dfs(int x) {
if (x == 5) {
int cnt = 0;
for (int i = 0; i < 6; ++i)
cnt += stat[row[i]];
res = max(cnt, res);
return;
}
for (int ad = 0; ad < 10; ++ad) {
dfs(x + 1);
for (int i = 0; i < 6; ++i) {
tab[i][x]++;
row[i] += ten[5 - x];
if (tab[i][x] > 9) {
tab[i][x] -= 10;
row[i] -= ten[6 - x];
}
}
}
}
void solve() {
memset(row, 0, sizeof(row));
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j) {
scanf("%d", &tab[i][j]);
row[i] = row[i] * 10 + tab[i][j];
}
}
res = 0;
for (int i = 0; i < 10; ++i) {
dfs(0);
for (int j = 0; j < 6; ++j) {
tab[j][j]++;
row[j] += ten[5 - j];
if (tab[j][j] > 9){
tab[j][j] -= 10;
row[j] -= ten[6 - j];
}
}
}
printf("%d\n", res);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("D:\\in.txt", "r", stdin);
#endif
init();
solve();
return 0;
}
/* 下面是我之前乱搞的程序,虽然AC了,但是是不正确的
反例:
9 3 5 9 6 7
6 8 9 7 9 5
9 9 9 5 9 9
3 6 5 6 9 9
9 5 9 9 7 9
8 6 9 9 8 8
*/
/*
int tab[6][6], res;
int lin[6];
bool gao(int x, int y, int t) {
for (int i = 0; i < 6; ++i) {
tab[x][i] += t;
if (tab[x][i] > 9)
tab[x][i] -= 10;
}
int add, best = 0;
for (int ad = 0; ad < 10; ++ad) {
int cnt = 0;
for (int i = 0; i < 6; ++i) {
if (i == y) continue;
if (tab[x][i] + ad > 9)
cnt += tab[x][i] + ad - 10;
else
cnt += tab[x][i] + ad;
}
int adx = 10 - ad;
for (int i = 0; i < 6; ++i) {
if (i == x) continue;
if (tab[i][y] + adx > 9)
cnt += tab[i][y] + adx - 10;
else
cnt += tab[i][y] + adx;
}
if (best < cnt) {
best = cnt;
add = ad;
}
}
if (add) {
for (int i = 0; i < 6; ++i)
if (tab[x][i] + add > 9)
tab[x][i] += add - 10;
else
tab[x][i] += add;
add = 10 - add;
for (int i = 0; i < 6; ++i)
if (tab[i][y] + add > 9)
tab[i][y] += add - 10;
else
tab[i][y] += add;
return true;
}
return false;
}
void calc() {
int t;
bool flag;
do {
flag = false;
for (int i = 0; i < 6; ++i) {
t = 9 - tab[i][lin[i]];
if (gao(i, lin[i], t)) flag = true;
}
} while (flag);
int cnt = 0;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j)
cnt += tab[i][j];
}
res = max(res, cnt);
}
void dfs(int x) {
if (x == 6) {
calc();
return;
}
for (int i = 0; i < 6; ++i) {
if (lin[i] != -1) continue;
lin[i] = x;
dfs(x + 1);
lin[i] = -1;
}
}
void solve() {
int tt = 0;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j) {
scanf("%d", &tab[i][j]);
tt += tab[i][j];
}
}
res = 0;
memset(lin, -1, sizeof(lin));
for (int i = 0; i < 10; ++i) {
for (int k = 0; k < 6; ++k)
if (tab[k][k] == 9) tab[k][k] = 0;
else tab[k][k]++;
dfs(0);
}
printf("%d\n", res);
printf("%d\n", tt);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("D:\\in.txt", "r", stdin);
#endif
// int t = clock();
solve();
// printf("%dms\n", clock() - t);
return 0;
}
*/
下面这份代码加了剪枝,每次列增加1的时候看最优情况是否优于已知结果,不过剪枝效果并不明显
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <bitset>
#include <cmath>
#include <set>
#include <utility>
#include <ctime>
#define sqr(x) ((x)*(x))
using namespace std;
const int N = 1000000;
int tab[6][6], res, row[6];
int stat[N];
int speed[7][N];
int ten[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
void init() {
memset(stat, -1, sizeof(stat));
int t[6], st[10];
memset(t, 0, sizeof(t));
for (int i = 0; i < 100000; ++i) {
int p = 0;
while (t[p] == 9) t[p++] = 0;
if (i) t[p]++;
int best = 0, cnt, s = i;
for (int add = 0; add < 10; ++add) {
cnt = 0;
for (int j = 0; j < 6; ++j) {
t[j]++;
s += ten[j];
if (t[j] > 9){
t[j] -= 10;
s -= ten[j + 1];
}
cnt += t[j];
}
best = max(best, cnt);
st[add] = s;
}
for (int j = 0; j < 10; ++j)
stat[st[j]] = best;
}
memset(speed, 0, sizeof(speed));
for (int i = 0; i < N; ++i) {
speed[5][i] = stat[i];
speed[4][i / 10] = max(speed[4][i / 10], stat[i]);
speed[3][i / 100] = max(speed[3][i / 100], stat[i]);
speed[2][i / 1000] = max(speed[2][i / 1000], stat[i]);
speed[1][i / 10000] = max(speed[1][i / 10000], stat[i]);
}
}
void dfs(int x) {
if (x > 1 && res > 0 || x == 5) {
int cnt = 0;
for (int i = 0; i < 6; ++i) {
if (x != 5)
cnt += speed[x - 1][row[i] / ten[6 - x]];
else
cnt += speed[x][row[i]];
}
if (x == 5) {
res = max(cnt, res);
return ;
} else if (cnt <= res) {
return;
}
}
for (int ad = 0; ad < 10; ++ad) {
dfs(x + 1);
for (int i = 0; i < 6; ++i) {
tab[i][x]++;
row[i] += ten[5 - x];
if (tab[i][x] > 9) {
tab[i][x] -= 10;
row[i] -= ten[6 - x];
}
}
}
}
void solve() {
memset(row, 0, sizeof(row));
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j) {
scanf("%d", &tab[i][j]);
row[i] = row[i] * 10 + tab[i][j];
}
}
res = 0;
for (int i = 0; i < 10; ++i) {
dfs(0);
for (int j = 0; j < 6; ++j) {
tab[j][j]++;
row[j] += ten[5 - j];
if (tab[j][j] > 9){
tab[j][j] -= 10;
row[j] -= ten[6 - j];
}
}
}
printf("%d\n", res);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("D:\\in.txt", "r", stdin);
#endif
init();
solve();
return 0;
}
Step 1: Naieve Approach. It’s always best to start a problem by trying the most obvious thing.Unfortunately, here there is not even any clear approach, because we are allowed “an unlimitednumber of hypnotic suggestions.” So how do we know when we are done? This is definitely a problem!As we can’t get anywhere without doing something about this, we should start by focusingour efforst here.
Step 2: Make observations to limit the search space. When a problem appears difficultto approach algorithmically, it’s often best to try to make some observations about the particularsituation that allows one to consider less cases than before. Here, we note that the order ofhypnotisms doesn’t matter (since addition is commutative) and furthermore that ten of the samehypnotism is the same as none at all, so we at least now have a finite number of cases to check.Returning to the naieve approach, we should see exactly how many cases there are. There are 10possibilities for each row, column, and the major diagonal, for a total of 10^13 possibilities. This isdefinitely too much, so we should see what else we can do.
Step 3: Try to improve on the naieve approach. 10^13 is way too much, but 10^7 is invitinglywithin our limit on number of operations. Note that we don’t actually need to try all the possibilitiesfor the rows, since once we have made our choices for the columns and main diagonal, therows behave independently. In fact, as there are only 10^6 possibilities for what a row looks like, wecan optimize further by precomputing, for each row, what the best achievable value is. This getsus down to the 10^7 operations we wanted.
Step 4: Optimize further. However, 10^7 is close to the limit, so we make one more optimizationjust in case. Note that we really haven’t exploited any of the symmetry of the situation yet. We do1so now. Since applying a hypnotism to every column is essentially the same as applying 9 to eachrow, we can assume that no hypnotisms are applied to the first column (else apply 9 hypnotisms onevery row and 1 on every column until we have cancelled out the hypnotisms that we had initiallyapplied in the first column). This lets us cut off another factor of 10, so that we now only use 10^6operations, which is well within our bounds.