题意:
在一个n*m(n,m<=15)的网格中,每个格子有一个值,现在从网格中取出k(k<=n*m)个,
保证在选中的格子中从任意一个格子去另外的所有格子(路径上的点也要是选出的格子)最多只用到四种(上,下,左,右)操作中的两种,
并使得到的值最大。输出该值和选中的格子坐标。
分析:
因为路径上的所有点必须在选出的格子内,且只能有两个方向,所以所有选出来的点必定可以组成一个凸多边形。
设f[i][p][q][l][r][s],i为当前1到i层,选p到q的所有数,左(l)右(r)边是否可以继续延伸(0可以,1不行),已经选了s个数的最大权值和。
根据我们只能构成凸多边形,假定f[i][p][q][l][r][s]由f[i'][p'][q'][l'][r'][s']转移来,如果r'为1,r也为1;如果r'为0,但q<q',r为1;否则为0.
状态数O(N^2M^3),时间复杂度O(N^2M^5),看似会超时,但实际上能够转移的状态没那么多。
在一个n*m(n,m<=15)的网格中,每个格子有一个值,现在从网格中取出k(k<=n*m)个,
保证在选中的格子中从任意一个格子去另外的所有格子(路径上的点也要是选出的格子)最多只用到四种(上,下,左,右)操作中的两种,
并使得到的值最大。输出该值和选中的格子坐标。
分析:
因为路径上的所有点必须在选出的格子内,且只能有两个方向,所以所有选出来的点必定可以组成一个凸多边形。
设f[i][p][q][l][r][s],i为当前1到i层,选p到q的所有数,左(l)右(r)边是否可以继续延伸(0可以,1不行),已经选了s个数的最大权值和。
根据我们只能构成凸多边形,假定f[i][p][q][l][r][s]由f[i'][p'][q'][l'][r'][s']转移来,如果r'为1,r也为1;如果r'为0,但q<q',r为1;否则为0.
状态数O(N^2M^3),时间复杂度O(N^2M^5),看似会超时,但实际上能够转移的状态没那么多。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 16, MAXM = 16, MAXK = 226;
int f[MAXN][MAXM][MAXM][2][2][MAXK];
int step[MAXN][MAXM][MAXM][2][2][MAXK], ansflag;
int er[7] = {0, 16, 256, 4096, 8192, 16384, 4194304};
int pre[MAXN][MAXM];
int n, m, k, map[MAXN][MAXN];
int oil = 0;
void makestep(int &t, int i, int p, int q, int l, int r, int s)
{
t = i+p*er[1]+q*er[2]+l*er[3]+r*er[4]+s*er[5];
}
void deal(int _i, int _j, int _k, int _l, int _r, int _s)
{
for(int p = _l?_j:1, l, r, tmp; p <= _k; ++p)
for(int q = max(p, _j), mm = _r?_k:m; q <= mm && q-p+1+_s <= k; ++q)
{
l = _l, r = _r;
if(!l && p > _j) l = 1;
if(!r && q < _k) r = 1;
tmp = f[_i][_j][_k][_l][_r][_s]+pre[_i+1][q]-pre[_i+1][p-1];
if(tmp > f[_i+1][p][q][l][r][_s+q-p+1])
{
f[_i+1][p][q][l][r][_s+q-p+1] = tmp;
makestep(step[_i+1][p][q][l][r][_s+q-p+1], _i, _j, _k, _l, _r, _s);
if(_s+q-p+1 == k && tmp > oil)
{
oil = tmp;
makestep(ansflag, _i+1, p, q, l, r, _s+q-p+1);
}
}
}
}
void find_path(int cur)
{
int sta = cur;
if(sta%er[1]) find_path(step[sta%er[1]][sta%er[2]/er[1]][sta%er[3]/er[2]][sta%er[4]/er[3]][sta%er[5]/er[4]][sta%er[6]/er[5]]);
else return;
for(int i = sta%er[2]/er[1], lim = sta%er[3]/er[2]; i <= lim; ++i)
cout << (sta%er[1]) << ' ' << i << endl;
}
int main()
{
cin >> n >> m >> k;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
cin >> map[i][j];
pre[i][j] = pre[i][j-1]+map[i][j];
}
memset(f[1], 0xfffff, sizeof(f[1]));
for(int p = 1; p <= n; ++p)
for(int i = 1, l, r; i <= m; ++i)
for(int j = i; j <= m && j-i+1 <= k; ++j)
{
l = r = 1;
if(i > 1) l = 0;
if(j < m) r = 0;
f[p][i][j][l][r][j-i+1] = pre[p][j]-pre[p][i-1];
if(j-i+1 == k && f[p][i][j][l][r][j-i+1] > oil)
{
oil = f[p][i][j][l][r][j-i+1];
makestep(ansflag, p, i, j, l, r, j-i+1);
}
}
for(int i = 1; i < n; ++i)
for(int p = 1; p <= m; ++p)
for(int q = p; q <= m; ++q)
for(int l = q-p+1; l <= k; ++l)
{
if(f[i][p][q][0][0][l] != -1) deal(i, p, q, 0, 0, l);
if(f[i][p][q][0][1][l] != -1) deal(i, p, q, 0, 1, l);
if(f[i][p][q][1][0][l] != -1) deal(i, p, q, 1, 0, l);
if(f[i][p][q][1][1][l] != -1) deal(i, p, q, 1, 1, l);
}
cout << "Oil : " << oil << endl;
find_path(ansflag);
return 0;
}