標籤:style blog http 使用 os io for ar
時間限制:0.75s
空間限制:6M
題意:
在一個n*m(n,m<=15)的網格中,每個格子有一個值,現在從網格中取出k(k<=n*m)個,保證在選中的格子中從任意一個格子去另外的所有格子最多隻用到四種(上,下,左,右)操作中的兩種,並使得到的值最大。輸出該值和選中的格子座標。
Solution
從選中的網格的任意一個去所有格子只用兩種操作:
左圖一種滿足條件的網格集合,右圖的(1,3)到(3,2)必需使用(左,下,右)三種操作才能到達,是不符合要求的。
注意到,滿足條件的網格集合一定不會出現凹形,因為凹形的上下兩個突出點中的格子不可能只用兩個操作到達。
由此用0,1記錄左右兩邊是 向外突出還是向內收縮。
當一行的一邊向內收縮時便不可以再向外突出了。
接下來是按行dp
f[row][l][r][opl][opr][k],代表第row行,取第l列帶第r列的格子,左邊格子突出狀態為opl,右邊為opr,當前共選擇了k個格子
記錄路徑時如果用結構體記錄下所有的狀態的上一個狀態需要15*15*2*2*225 * size(int)*6的空間.
爆一次記憶體後,想到對於上一個狀態row和k可以推出,只需要記錄(l,r,opl,opr)即可,而(l,r,opl,opr)都是不超過16的數,那麼可以用一個int形用16進位存下
存的時候用 record=(l << 12) + (r << 8) + (opl << 4) + (opr)
用的時候
上一個(row,l,r,opl,opr,k)
可以由當前的(row - 1, (record >> 12) % 16, (record >> 8) % 16, (record >> 4) % 16, record % 16, k - r + l - 1)得到
這道題需要注意的地方有很多,不一一列舉.總的來說是一道值得一做的好題
參考代碼:
#include <iostream>#include <cstring>#include <cstdio>#define pdx pr[row][l][r][opl][opr][k]#define val(r,a,b) g[r][b] - g[r][a - 1]using namespace std;int f[16][16][16][2][2][230], g[16][16];int pr[16][16][16][2][2][230];int n, m, k, ans;int a, al, ar, aol, aor;/* op=0 / 左突 op=1 \ 右突*///目前狀態向下更新void update (int row, int l, int r, int opl, int opr, int s) {if (row == n) return ;for (int nl = (opl == 0 ? 1 : l); nl <= r; nl++)for (int nr = max (l, nl); nr <= (opr == 0 ? r : m); nr++) {int t1, t2;if (nl == l) t1 = opl;elset1 = (nl < l ? 0 : 1);if (nr == r) t2 = opr;elset2 = (nr < r ? 0 : 1);if (f[row + 1][nl][nr][t1][t2][s + (nr - nl + 1)] < f[row][l][r][opl][opr][s] + val (row + 1, nl, nr) ) {f[row + 1][nl][nr][t1][t2][s + (nr - nl + 1)] = f[row][l][r][opl][opr][s] + val (row + 1, nl, nr);//記錄上一個狀態 , 16進位狀態壓縮pr[row + 1][nl][nr][t1][t2][s + (nr - nl + 1)] = (l << 12) + (r << 8) + (opl << 4) + (opr);}}}void dp() {for (int row = 1; row <= n; row++)for (int l = 1; l <= m; l++)for (int r = l; r <= m; r++)for (int opl = 0; opl < 2; opl++)for (int opr = 0; opr < 2; opr++) {f[row][l][r][opl][opr][r - l + 1] = g[row][r] - g[row][l - 1];for (int s = r - l + 1; s <= k; s++)if (f[row][l][r][opl][opr][s] > 0 ) {update (row, l, r, opl, opr, s);if (s == k)//記錄最大解if (f[row][l][r][opl][opr][s] > ans) {ans = f[row][l][r][opl][opr][s];a = row, al = l, ar = r, aol = opl, aor = opr;}}}}//輸出void write (int row, int l, int r, int opl, int opr, int k) {if (row == 0 || k <= 0) return;for (int i = l; i <= r; i++)printf ("%d %d\n", row, i);write (row - 1, (pdx >> 12) % 16, (pdx >> 8) % 16, (pdx >> 4) % 16, pdx % 16, k - r + l - 1);}int main() {#ifndef ONLINE_JUDGEfreopen ("in.txt", "r", stdin);#endif // oline_judgescanf ("%d %d %d", &n, &m, &k);for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {scanf ("%d", &g[i][j]);g[i][j] += g[i][j - 1];}memset (f, -1, sizeof f);dp();printf ("Oil : %d\n", ans);write (a, al, ar, aol, aor, k);return 0;}