【cofun1375】麦田
Description
话说公元199年,曹操准备与袁绍进行官渡决战。为整肃军纪,曹操下令:“全军将士,上至统帅,下至马佚,行军训练,不准践踏庄稼,不准倒犯民利,违令者斩首。”遇有麦场,骑兵下马,扶麦而行。百姓见状,交口称赞。
不巧,在一次出巡时,曹操乘坐的战马受惊,跃入麦田,践踏了一片麦苗。曹操当即下马,请求主簿依军令将自己斩首示众。在众将领劝说下,割发代首,以儆效尤,此事才算了结。
然而,为了赢得官渡之战,曹操又遇到了麻烦。为了对袁绍的一路军队进行埋伏,需要在麦田中设立几个埋伏点,而且几个点之间,还需要有道路相通。埋伏点和道路均会将所在格子麦苗破坏掉。这可难为了刚刚割发代首的曹孟德。经过重重的抉择,曹操发布了一个刚刚作出的艰难决定:“按破坏麦苗数加倍赔偿”。 作为曹操的得力军师,你希望为主公分忧,妥善解决这个问题:
埋伏所在麦田分成了n*m方格,每格麦苗数不同。其中有k个格子将作为埋伏点,需要设立道路,使得每2个埋伏点之间均相通。主公希望剩下的麦苗越多越好。
Input Format
第一行 n m k(1<=n,m<=100 nm<=200 1<=k<=7)。 接下来为nm的矩阵,表示第i行j列的格子的麦苗数。 最后k行,每行包含2个整数,表示(i,j)格为埋伏点。(0<=i < n 0< = j < m)
Output Format
输出麦苗最多能剩余多少
Sample Input
4 4 4
2 7 9 5
4 3 5 5
7 6 7 5
8 2 1 7
1 0
0 1
1 1
0 2
Sample Output
60
分析:
k <= 7,类似棋盘,考虑状压DP。用f[i][x][y]表示当前设置埋伏状态为i(0:未设/ 1:设),到达格子为(x,y)时需要破坏的最少麦苗。
状压DP,转移方程:
f[i][x][y] = min(f[i][x][y], f[i - j][x][y] + f[j][x][y] - a[x][y]);
- 注意到每次循环完状态i应做一遍类似spfa的bfs,把到达其他格子时状态相同的情况计算出来。
- 代码:
#include <bits/stdc++.h>
using namespace std;
const int g[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int n, m, k, x, y, i, j, head, tail, s, ans, a[105][105], q[10005][2], f[1 << 8][105][105];
bool v[105][105];
inline int read()
{
int x = 0, w = 1;
char ch = 0;
while(ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
x = x * 10 + ch - '0', ch = getchar();
return x * w;
} //读入优化
inline void write(int x)
{
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}//输出优化
inline void spfa(int p)
{
int i, x1, y1, x2, y2;
for(head = 1; head <= tail; head ++)
{
x1 = q[head][0], y1 = q[head][1];
v[x1][y1] = false;
for(i = 0; i < 4; i ++)
{
x2 = x1 + g[i][0], y2 = y1 + g[i][1];
if (x2 >= 0 && x2 < n && y2 >= 0 && y2 < m)
if(f[p][x2][y2] > f[p][x1][y1] + a[x2][y2])
{
f[p][x2][y2] = f[p][x1][y1] + a[x2][y2];
if (! v[x2][y2])
v[x2][y2] = true, q[++ tail][0] = x2, q[tail][1] = y2;
}
}
}
}
int main()
{
n = read(), m = read(), k = read();
for(i = 0; i < n; i ++)
for(j = 0; j < m; j ++)
{
a[i][j] = read();
ans += a[i][j];
}
memset(f, 0x3f, sizeof(f));
for(i = 0; i < k; i ++)
{
x = read(), y = read();
f[1 << i][x][y] = a[x][y];
}
//读入&初始化
for(i = 1; i < (1 << k); i ++)
{
tail = 0;
for(x = 0; x < n; x ++)
for(y = 0; y < m; y ++)
{
for(j = 1; j <= i; j ++)
if ((i & j) == j)
f[i][x][y] = min(f[i][x][y], f[i - j][x][y] + f[j][x][y] - a[x][y]);
if (f[i][x][y] != 0x3f3f3f3f)
q[++ tail][0] = x, q[tail][1] = y, v[x][y] = true;
}
spfa(i);
}
//状压DP
s = 0x3f3f3f3f;
for(x = 0; x < n; x ++)
for(y = 0; y < m; y ++)
s = min(s, f[(1 << k) - 1][x][y]);
ans -= s;
write(ans);
//比较并输出答案
return 0;
}
新的一天~【女生每月必备的腰酸。。OTL。。太菜了吧。忽视它忽视它忽视它。。