dp引入 解题报告

dp引入

[IOI1994]数字三角形 Number Triangles

问题描述:略。

转移方程:
F ( i , j ) = A [ i , j ] + m a x { F ( i − 1 , j ) F ( i − 1 , j − 1 ) i f j > 1 F(i, j) = A[i, j] + max \begin{cases} F(i - 1, j) \\ F(i - 1, j - 1)\quad if\quad j > 1 \end{cases} F(i,j)=A[i,j]+max{F(i1,j)F(i1,j1)ifj>1
状态表示:

F(i, j)表示从左上角走到第i行第j列的和得最大值。

边界:
F ( i , j ) = A [ i , j ] F(i, j) = A[i, j] F(i,j)=A[i,j]
目标:
m a x 1 ≤ j ≤ N F ( N , j ) max_{1\leq j\leq N}{F(N, j)} max1jNF(N,j)
代码:

void solve() {
    int n; cin>>n;
    vector<vector<int>> ph(n+1, vector<int>(n+1));
    auto f(ph);
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= i; ++j) {
            cin>>ph[i][j];
        }
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= i; ++j) {
            f[i][j] = max(f[i-1][j], f[i-1][j-1]) + ph[i][j];
        }
    }
    cout<<*max_element(all(f[n]));
}

最长上升子序列(LIS)

问题描述:略。

转移方程:
F ( i ) = m a x 0 ≤ j < i , A [ j ] < A [ i ] F [ j ] + 1 F(i) = max_{0 \leq j < i,A[j]F(i)=max0j<i,A[j]<A[i]F[j]+1
状态表示:

F(i)表示前i个最上上升子序列个数

边界:
F ( 0 ) = 0 F(0) = 0 F(0)=0
目标:
m a x 1 ≤ i ≤ N F [ i ] max_{1 \leq i \leq N}{F[i]} max1iNF[i]
代码:

void solve() {
    int n; cin>>n;
    vector<int> a(n+1);
    for(int i = 1; i <= n; ++i) cin>>a[i];
    vector<int> f(n+1);
    for(int i = 1; i <= n; ++i) {
        f[i] = 1;
        for(int j = 1; j < i; ++j) {
            if(a[j] <= a[i]) f[i] = max(f[i], f[j] + 1);
        }
    }
    cout<<*max_element(all(f));
}
void solve() {
    int n; cin>>n;
    vector<int> a(n);
    for(auto &t: a) cin>>t;
    vector<int> f;
    for(auto t: a) {
        auto pos = lower_bound(all(f), t);
        if(pos == f.end()) f.push_back(t);
        else *pos = t;
    }
    cout<<f.size();
}

最长公共子序列(LCS)

问题描述:略。

转移方程:
F ( i , j ) = m a x { F ( i − 1 , j ) F ( i , j − 1 ) F ( i − 1 , j − 1 ) i f A [ i ] = B [ i ] F(i, j) = max \begin{cases} F(i - 1, j) \\ F(i, j - 1) \\ F(i - 1, j - 1) \quad if \quad A[i] = B[i] \end{cases} F(i,j)=max F(i1,j)F(i,j1)F(i1,j1)ifA[i]=B[i]
状态表示:

​ 在A串的第i个和B串的第j个中的最长公共子序列

边界:
F ( i , 0 ) = F ( 0 , j ) = 0 F(i, 0) = F(0, j) = 0 F(i,0)=F(0,j)=0
目标:
F ( N i , M j ) F(N_{i}, M_{j}) F(Ni,Mj)
代码:

// 字符串和数字对于这题来说相同
void solve() {
    int n; cin>>n;
    vector<int> A(n+1);
    auto B(A);
    vector<vector<int>> f(n+1, vector<int>(n+1));
    rep(i,1,n) cin>>A[i];
    rep(i,1,n) cin>>B[i];
    rep(i,1,n) {
        rep(j,1,n) {
            f[i][j] = max(f[i-1][j], f[i][j-1]);
            if(A[i] == B[j]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);
        }
    }
    cout<<f[n][n];
}

过河卒

问题描述:略。

转移方程:
F ( i , j ) = { 0 i f p h [ i , j ] = 马能到达位置 F ( i − 1 , j ) + F ( i , j − 1 ) F(i, j) = \begin{cases} 0 \quad if \quad ph[i,j] = 马能到达位置 \\ F(i-1,j) + F(i,j-1) \end{cases} F(i,j)={0ifph[i,j]=马能到达位置F(i1,j)+F(i,j1)
状态表示:

F(i,j)表示从0,0点到坐标(i,j)的路径之和。

边界:
F ( 0 , 0 ) = 1 F(0,0) = 1 F(0,0)=1
目标:
F ( N , N ) F(N,N) F(N,N)
代码:

void solve() {
    int x1,x2,y1,y2; cin>>x1>>y1>>x2>>y2;
    x1++,y1++,x2++,y2++;
    vector<vector<int>> f(x1+1, vector<int>(y1+1));
    vector<int> fx({-1,-1,1,1,2,2,-2,-2}), fy({2,-2,2,-2,1,-1,1,-1});
    for(int i = 0; i < 8; ++i) {
        int xx = x2 + fx[i], yy = y2 + fy[i];
        if(xx < 1 || xx > x1 || yy < 1 || yy > y1) continue;
        f[xx][yy] = -1;
    }
    f[x2][y2] = -1;
    // f[0][2] = f[2][0] = 1;
    for(int i = 1; i <= x1; ++i) {
        for(int j = 1; j <= y1; ++j) {
            if(i == 1 && j == 1) {
                f[i][j] = 1; continue;
            }
            if(f[i][j] == -1) f[i][j] = 0;
            else f[i][j] = f[i-1][j] + f[i][j-1];
            // cout<
        }
    }
    cout<<f[x1][y1];
}

luogu数据:

in: 8 6 0 4
out: 1617

错误原因:判断方向continue错了,xx > x2 || yy > y2,原来是xx >= x2 || yy >= y2

练习

P1057 [NOIP2008 普及组] 传球游戏

问题描述:传球游戏,开始球在第一个人处,问经过m次传球,传到第一个人处的方法数。

转移方程:
F ( j = = 1 ? n : j − 1 , i ) + = F ( j , i − 1 ) F ( j = = n ? 1 : j + 1 , i ) + = F ( j , i − 1 ) F(j == 1 ? n : j -1 ,i) += F(j, i - 1)\\ F(j == n ? 1 : j + 1, i) += F(j, i - 1) F(j==1?n:j1,i)+=F(j,i1)F(j==n?1:j+1,i)+=F(j,i1)
状态表示:

F(j,i)表示从0到第i次传到第j个人的方法数。因为是成环,需要在左右边界进行特判。

边界:
F ( 1 , 0 ) = 1 F(1,0) = 1 F(1,0)=1
目标:
F ( 1 , m ) F(1,m) F(1,m)
代码:

void solve() {
    int n,m; cin>>n>>m;
    vector<vector<int>> f(n+1, vector<int>(m+1));
    f[1][0] = 1;
    rep(i,1,m) { // ** 
        rep(j,1,n) {
            f[j == 1 ? n : j - 1][i] += f[j][i-1];
            f[j == n ? 1 : j + 1][i] += f[j][i-1];
        }
    }
    cout<<f[1][m];
}

 错误原因

  •  没有理清子问题到底是什么。开始将子问题理解成,第j个人经i次传到下一次。但是真正的子问题是:第i次传球传给的人。 将n和m遍历顺序进行修改就正确了

最大子段和

问题描述:给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

转移方程:
F ( i ) = m a x ( A [ i ] , F ( i − 1 ) + A [ i ] ) F(i) = max(A[i], F(i-1) + A[i]) F(i)=max(A[i],F(i1)+A[i])
状态表示:

F(i)表示的是前i个连续的最大字段和。

边界:
F ( 1 ) = A [ 1 ] F(1) = A[1] F(1)=A[1]
目标:
m a x 1 ≤ i ≤ N F ( i ) max_{1 \leq i \leq N}{F(i)} max1iNF(i)
代码:

void solve() {
    int n; cin>>n;
    vector<LL> a(n+1);
    for(int i = 1; i <= n; ++i) cin>>a[i];
    vector<LL> f(n+1);
    f[1] = a[1];
    for(int i = 2; i <= n; ++i) {
        f[i] = max(a[i], f[i-1] + a[i]);

    }
    cout<<*max_element(f.begin()+1, f.end());

}

你可能感兴趣的:(算法题,动态规划,算法)