1. 程式人生 > >dp算法之方格取數

dp算法之方格取數

想想 規劃 方向 最大的 出發 ace for while using

動態規劃算法通常基於一個遞推公式及一個或多個初始狀態。當前子問題的解將由上一次子問題的解推出。使用動態規劃來解題只需要多項式時間復雜度,因此它比回溯法、暴力法等要快許多。

現在我們用一道題來了解它。

dp經典之方格取數
【問題描述】

設有N*N的方格圖(N<=10,我們將其中的某些方格中填入正整數,而其他的方格中則放入數字0。如下圖所示(見樣例):)

0 0 0 0 0 0 0 0
0 0 13 0 0 6 0 0
0 0 0 0 7 0 0 0
0 0 0 14 0 0 0 0
0 21 0 0 0 4 0 0
0 0 15 0 0 0 0 0
0 14 0 0 0 0 0 0
0 0 0 0 0 0 0 0

某人從圖的左上角的A 點出發,可以向下行走,也可以向右走,直到到達右下角的B點。在走過的路上,他可以取走方格中的數(取走後的方格中將變為數字0)。

此人從A點到B 點共走兩次,試找出2條這樣的路徑,使得取得的數之和為最大。

【輸入文件】

輸入的第一行為一個整數N(表示N*N的方格圖),接下來的每行有三個整數,前兩個表示位置,第三個數為該位置上所放的數。一行單獨的0表示輸入結束。

【輸出文件】

只需輸出一個整數,表示2條路徑上取得的最大的和。

【輸入樣例】

8

2 3 13

2 6 6

3 5 7

4 4 14

5 2 21

5 6 4

6 3 15

7 2 14

0 0 0

【輸出樣例】

67

我們回到動態規劃的本質來看代問題,我們在想想這個問題的狀態,對於走一次,走到矩陣的任意一個位置就是一個狀態,而要是走兩次,顯然走到矩陣的某個位置只是一個狀態的一部分,不能完整的描述整個狀態。那另一部分顯然就是第二次走到的位置了。如果我們把這兩部分合起來就是一個完整的狀態了。

於是,設計一個狀態opt[i1,j1,i2,j2]表示兩條路分別走到(i1,j1)點和(i2,j2)點時取到的最大值。顯然決策有4中(乘法原理一個點兩種*另一個點的兩中)

即(上,上)(上,左)(左,上)(左,左)上和左表示從哪個方向走到該點,當然要註意走到同行,同列,同點時的情況(因為要求路徑不重復)。

【狀態】f[i][j][k][l]表示兩人分別到(i,j)、(k,l)所取過的數的和.G[i][j]表示方格裏的數.

【方程】f[i][j][k][l] = max{f[i-1][j][k-1][l], f[i-1][j][k][l-1], f[i][j-1][k-1][l], f[i][j-1][k][l-1]}+G[i][j]+(i==k&&j==l ? 0 : G[k][l])


再來簡潔的分析一下:
一個四重循環枚舉兩條路上分別走到的位置。因為每個點均從上或左繼承而來,故內部有4個if,分別表示兩個點從上上、上左,左上、左左繼承來時,加上當前兩個點所取得的最大值。a[i][j]表示(i,j)上的值,sum[i][j][h][k]表示第一條路走到(i,j),第二條路走到(h,k)時的最優解。
比如sum[i][j][h][k]=max(sum[i][j][h][k],sum[i-1][j][h-1][k]+ai][j]+a[h][k])表示兩點均從上面走來。

好,想必已經迫不及待了,上代碼!

#include<cstdio>
#include<algorithm>
using namespace std;
int a[51][51];
int sum[51][51][51][51];
int n,i,j,h,k,x,y,z;
int main()
{
  scanf("%d%d%d%d",&n,&x,&y,&z);
  while (x && y && z) //非0即真
  {
  a[x][y]=z;
  scanf("%d%d%d",&x,&y,&z);
  }
  //開始dp
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
      for (int h=1;h<=n;h++)
        for (int k=1;k<=n;k++)
        {
          int tmp1=max(sum[i-1][j][h-1][k],sum[i][j-1][h][k-1]);
          int tmp2=max(sum[i-1][j][h][k-1],sum[i][j-1][h-1][k]);
          sum[i][j][h][k]=max(tmp1,tmp2)+a[i][j];
          if (i!=h && j!=k) sum[i][j][h][k]+=a[h][k];
        }
  printf("%d\n",sum[n][n][n][n]);
  return 0;
}

dp算法之方格取數