☆【動態規劃】木馬級守護進程

来源:互联网 发布:河源网络刷手骗局 编辑:程序博客网 时间:2024/06/09 17:19
【问题描述】   “哈哈哈,大功告成,木马级守护进程,欢迎来破。”欧教如是说。   欧教自从使用了新的教师端守护进程之后,要发现在机房偷偷玩游戏的同学简直是易如反掌。对于欧教而言,要使打游戏的同学改邪归正的最好办法就是在他打游戏的时候通过教师端关掉他的电脑。   但是由于欧教的关机程序写的不好,因此如果要关机,就必须关掉机房中一个矩形区域的所有电脑(矩形大小任意),而且在使用了k 次关机程序之后,欧教的电脑就会自动崩溃。    机房是一个矩形区域,有 n 行 m 列。每台电脑都有一个关机价值,当然机房还有很少的一部分同学没有玩游戏,因此他们的关机价值就为负数。由于欧教要写木马级关机进程,因此只有麻烦乐乐帮他算出在使用 k次关机程序后所能得到的最大关机价值和。  【输入数据】 第一行三个整数 n,m,k。 接下来 n 行每行 m个整数,表示每台电脑的关机价值。  【输出数据】 一个整数,表示最大关机价值和。  【样例输入】 4 2 2 -4 -1 22 18 -1 -4 30 10  【样例输出】 80  【数据范围及约定】 对于 40%  的数据:m=1; 对于 100%的数据:1≤n≤100,1≤m≤2,1≤k≤10。 k 次关机的区域可以重叠,但价值只算一次。 答案和中间值都在 longint范围内。
這是一道動態規劃題。

首先考慮m = 1的情況。
狀態:用f[i][j]表示前i行中關機j次所能得到的最大價值。

容易得到轉移方程:f[i][j] = max{f[i - 1][j], f[k][j - 1] + sum[i] - sum[k]}。
其中,sum[i]表示這一列i的前綴和。

再考慮m = 2的情況。
狀態:同上。

在計算f值之前先預處理出f0[L][R][j]和f1[L][R][j],分别表示第1,2列(L, R]这个区间关j次的最大收益。

那麼對於每一步的決策有四種:
 1) 不選;
 2) 選第一列的子矩形;
 3) 選第二列的子矩形;
 4) 選第一列且第二列的子矩形。

於是可得轉移方程:

f[i][j] = max{f[i - 1][j], f[k][j - 1] + sum1[i] - sum1[k] + sum2[i] - sum2[k], f[k][j - u - v] + f0[k][i][u] + f1[k][i][v]}。

其中sum1[i]和sum2[i]分別表示第一列和第二列i的前綴和。

Accode:
#include <cstdio>#include <cstring>#include <cstdlib>#include <bitset>const char fi[] = "mua.in";const char fo[] = "mua.out";const int maxN = 110;const int maxK = 20;const int MAX = 0x3fffff00;const int MIN = -MAX;int c[maxN][2];int sum[maxN][2];int f[maxN][maxK];int f0[maxN][maxN][maxK];int f1[maxN][maxN][maxK];int n, m, K, ans;  void init_file()  {    freopen(fi, "r", stdin);    freopen(fo, "w", stdout);  }  void readdata()  {    scanf("%d%d%d", &n, &m, &K);    for (int i = 1; i < n + 1; ++i)     for (int j = 0; j < m; ++j)      {        int x;        scanf("%d", &x);        sum[i][j] = sum[i - 1][j] + x;      }  }  void work1()  {    memset(f, 0x80, sizeof(f));    for (int j = 0; j < K + 1; ++j) f[0][j] = 0;    for (int i = 1; i < n + 1; ++i)     for (int j = 0; j <= K && j <= i; ++j)      {        f[i][j] = f[i - 1][j];        if (j) for (int k = 1; k < i + 1; ++k)          f[i][j] = std::max(f[i][j],            f[k][j - 1] + sum[i][0] - sum[k][0]);        ans = std::max(ans, f[i][j]);      }  }  void work2()  {    memset(f0, 0x80, sizeof(f0));    memset(f1, 0x80, sizeof(f1));    memset(f, 0x80, sizeof(f));    for (int L = 0; L < n; ++L)    {      for (int j = 0; j < K + 1; ++j)        f0[L][L][j] = f1[L][L][j] = 0;      for (int R = L + 1; R < n + 1; ++R)       for (int j = 0; j <= K; ++j)        {          f0[L][R][j] = f0[L][R - 1][j];          f1[L][R][j] = f1[L][R - 1][j];          if (j) for (int k = L; k < R + 1; ++k)          {            f0[L][R][j] = std::max(f0[L][R][j],              f0[L][k][j - 1] +              sum[R][0] - sum[k][0]);            f1[L][R][j] = std::max(f1[L][R][j],              f1[L][k][j - 1] +              sum[R][1] - sum[k][1]);          }        }    }    for (int j = 0; j < K + 1; ++j) f[0][j] = 0;    for (int i = 1; i < n + 1; ++i)     for (int j = 0; j <= K && j <= i; ++j)      {        f[i][j] = f[i - 1][j];        if (j)        {          for (int k = 1; k < i + 1; ++k)            f[i][j] = std::max(f[i][j],              f[k][j - 1] + sum[i][0] - sum[k][0]              + sum[i][1] - sum[k][1]);          for (int k = 1; k < i; ++k)           for (int u = 0; u <= j; ++u)            for (int v = 0; u + v <= j; ++v)              f[i][j] = std::max(f[i][j],                f[k][j - u - v] + f0[k][i][u]                + f1[k][i][v]);        }        ans = std::max(ans, f[i][j]);      }  }  void print() {printf("%d", ans); }int main(){  init_file();  readdata();  if (m == 1) work1(); else work2();  print();  exit(0);}

第二次做:

#include <cstdio>#include <algorithm>#include <cstdlib>#include <string>#define max(a, b) ((a) > (b) ? (a) : (b))const char fi[] = "mua.in", fo[] = "mua.out";const int maxN = 110, maxK = 20;const int MAX = 0x3f3f3f3f, MIN = ~MAX;int f[maxK][maxN][maxN], f1[maxK][maxN];int sum[maxN][2];int n, m, K;void init_file(){    freopen(fi, "r", stdin);    freopen(fo, "w", stdout);    return;}inline int getint(){    int res = 0; char tmp; bool sgn = 1;    do tmp = getchar();    while (!isdigit(tmp) && tmp != '-');    if (tmp == '-') {sgn = 0; tmp =getchar();}    do res = (res << 3) + (res << 1) + tmp - '0';    while (isdigit(tmp = getchar()));    return sgn ? res : -res;}void readdata(){    n = getint(); m = getint(); K = getint();    for (int i = 1; i < n + 1; ++i)    for (int j = 0; j < m; ++j)        (sum[i][j] = getint()) += sum[i - 1][j];    return;}int work1(){    for (int k = 1; k < K + 1; ++k)    for (int i = 1; i < n + 1; ++i)    {        f1[k][i] = f1[k][i - 1];        for (int j = 0; j < i; ++j)            f1[k][i] = max(f1[k][i], f1[k - 1][j]                           + sum[i][0] - sum[j][0]);    }    return f1[K][n];}int work2(){    for (int k = 1; k < K + 1; ++k)    for (int i = 0; i < n + 1; ++i)    for (int j = 0; j < n + 1; ++j)    if (i || j)    {        if (i == j)        {            f[k][i][j] = max(f[k][i][j - 1], f[k][i - 1][j]);            for (int t = 0; t < i; ++t)                f[k][i][j] = max(f[k][i][j], f[k - 1][t][t]                                 + sum[i][0] - sum[t][0]                                 + sum[i][1] - sum[t][1]);        }        else if (i > j)        {            f[k][i][j] = f[k][i - 1][j];            for (int t = 0; t < i; ++t)                f[k][i][j] = max(f[k][i][j], f[k - 1][t][j]                                 + sum[i][0] - sum[t][0]);        }        else if (i < j)        {            f[k][i][j] = f[k][i][j - 1];            for (int t = 0; t < j; ++t)                f[k][i][j] = max(f[k][i][j], f[k - 1][i][t]                                 + sum[j][1] - sum[t][1]);        }    }    return f[K][n][n];}int main(){    init_file();    readdata();    printf("%d\n", (m == 1) ? work1() : work2());    return 0;}

原创粉丝点击