1. 程式人生 > 其它 >題解 [USACO16DEC]Team Building P

題解 [USACO16DEC]Team Building P

這題其實沒什麼好記錄的,只是因為我被一個看上去有些神奇但實際上和我做法一樣的做法困擾了好久。

主要是這樣的一個方程 \(f_{i, j, k} = f_{i-1, j, k} + f_{i, j-1, k} - f_{i-1, j-1, k} + f_{i-1, j-1, k-1} \times [a_i > b_j]\)

然後 zyy 神仙寫下了這樣的程式碼併發了題解

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mo 1000000009
using namespace std;
int a[1005],b[1005],f[12][1005][1005],n,m,p;
int main(){
    scanf("%d%d%d",&n,&m,&p);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) scanf("%d",&b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    for (int i=0;i<=n;i++)
        for (int j=0;j<=m;j++) f[0][i][j]=1;
    for (int i=1;i<=p;i++){
        for (int j=1;j<=n;j++)
            for (int k=1;k<=m;k++)
                if (a[j]>b[k]) f[i][j][k]=f[i-1][j-1][k-1];
        for (int j=1;j<=n;j++)
            for (int k=1;k<=m;k++) f[i][j][k]=(f[i][j][k]+f[i][j][k-1])%mo;
        for (int j=1;j<=n;j++)
            for (int k=1;k<=m;k++) f[i][j][k]=(f[i][j][k]+f[i][j-1][k])%mo;
        }
    printf("%d",f[p][n][m]);
}

而我的程式碼是這樣的

#include <iostream>
#include <algorithm>
#define int long long
const int N = 1005, P = 1000000009;
int n, m, k, a[N], b[N], f[N][N][15], K;
signed main() {
    std::cin >> n >> m >> K;
    for (int i = 1; i <= n; i++) std::cin >> a[i];
    for (int i = 1; i <= m; i++) std::cin >> b[i];
    std::sort(a+1, a+1+n), std::sort(b+1, b+1+m);
    for (int i = 0; i <= n; i++) f[i][0][0] = 1;
    for (int j = 0; j <= m; j++) f[0][j][0] = 1;
        
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 0; k <= K; k++) {
                f[i][j][k] =  (f[i-1][j][k] + f[i][j-1][k] - f[i-1][j-1][k] + P) % P;
                if (k != 0 && a[i] > b[j]) f[i][j][k] = (f[i][j][k] + f[i-1][j-1][k-1]) % P;
            }
    std::cout << f[n][m][K];
}    

主要有意下幾點不同:

  1. 初始化不一樣。
  2. 迴圈順序不一樣。
  3. 轉移似乎也不一樣。

然後就困擾了我很久才搞明白這三點所謂不同其實一個也不成立。

  1. 初始化:其實只是我多跑了一遍而已,人家直接就把 \(0\) 的填上了。
  2. 迴圈順序 & 轉移:
    其實 \(k\) 這一層的先後在這題中根本沒有影響,我們的轉移是一模一樣的。
    容易發現我那個轉移實際上是在求字首和,所以要減去重複部分,而 zyy 神仙先將每一行字首和之後再把字首和字首和,根本沒有區別。
    但是在 dp 的角度來看而不從數值變換的角度來看這就有一些不同了,他的 dp 方程的意義不再是一直不變的,而是發生了轉換(這也是困擾我的原因),中間時候狀態變成了“FJ 的最後一個是這個,FP 用前幾個和其匹配的方案數”,等到再加了一次才變回原來的狀態。
啟示

從純數值角度看 dp 方程或許會有不一樣的發現!