动规基础方程整理

背包

1.填满型01背包(装箱,递推方式+单调队列,后效性用倒着枚举避开)

1
2
3
4
5
6
7
8
9
10
bool f[20001];
int n, a[31], v;
    scanf("%d%d", &v, &n);
    f[0] = 1;
    for(int i = 1; i <= n; ++i)
        {
        scanf("%d", &a[i]);
        for(int j = v; j >= a[i]; --j) f[j] |= f[j - a[i]];
        }
    for(int j = v; j >= 0; --j) if(f[j]) {printf("%d\n", v - j); break;}

 2.含价值的填满型01背包(改一下动规方程)

1
2
3
4
5
6
7
8
scanf("%d%d", &V, &n);
for(int i = 1; i <= n; ++i)
    {
    scanf("%d%d", &cost, &val);
    for(int j = V; j >= cost; --j)
        f[j] = max(f[j], f[j - cost] + val), ans = max(ans, f[j]);
    }
printf("%d\n", ans);

 3.含价值的填满型完全背包(改变枚举顺序)

1
2
3
4
5
6
7
8
scanf("%d%d", &V, &n);
for(int i = 1; i <= n; ++i)
    {
    scanf("%d%d", &cost, &val);
    for(int j = cost; j <= V; ++j)
        f[j] = max(f[j], f[j - cost] + val), ans = max(f[j], ans);
    }
printf("%d\n", ans);

 4.复杂的,有连锁关系的背包(金明,不改变方程,只添加循环和判断)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
scanf("%d%d", &V, &m);
for(int i = 1; i <= m; ++i)
    {
    int v, p, q;
    scanf("%d%d%d", &v, &p, &q);
    if(!q)
        {
        cost[++n][0] = v;
        weight[n][0] = p;
        ind[i] = n;
        }
    else{
        int zhu = ind[q];
        cost[zhu][++fu[zhu]] = v;
        weight[zhu][fu[zhu]] = p;
        }
    }
for(int zhu = 1; zhu <= n; ++zhu)
    {
    for(int j = V; j >= cost[zhu][0]; --j)
        {
        dp[j] = max(dp[j], dp[j - cost[zhu][0]] + cost[zhu][0] * weight[zhu][0]);
        if(fu[zhu] >= 1 && j >= cost[zhu][0] + cost[zhu][1])
            dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][1]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][1] * weight[zhu][1]);
        if(fu[zhu] >= 2 && j >= cost[zhu][0] + cost[zhu][2])
            dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][2]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][2] * weight[zhu][2]);
        if(fu[zhu] >= 2 && j >= cost[zhu][0] + cost[zhu][1] + cost[zhu][2])
            dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][1] - cost[zhu][2]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][1] * weight[zhu][1] + cost[zhu][2] * weight[zhu][2]);
        ans = max(ans, dp[j]);
        }
    }
printf("%d\n", ans);

 5.匹配型填满型完全背包(用几个单词匹配一句完整的话,和01的可达性求解相似,外重循环要枚举当前目标)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
char stand[6][10]={
{'o','n','e'},{'p','u','t','o','n'},
{'o','u','t'},{'o','u','t','p','u','t'},
{'i','n'},{'i','n','p','u','t'}};
int T, len, l[]={3,5,3,6,2,5};
char s[1000001];
bool f[1000001];
bool cmp(char *str, int leng, int pos)
    {
    for(int i = 0; i < leng; ++i)
        if(str[i] != s[pos + i]) return false;
    return true;
    }
bool jud()
    {
    len = strlen(s);
    f[0] = true;
    for(int i = 1; i <= len; ++i) f[i] = false;
    for(int j = 1; j <= len; ++j)
        for(int i = 0; i < 6; ++i)
            if(j >= l[i] && cmp(stand[i], l[i], j - l[i]))
                f[j] |= f[j - l[i]];
    return f[len];
    }
int main()
    {
    for(scanf("%d", &T); T; --T)
        {
        scanf("%s", s);
        puts(jud() ? "YES" : "NO");
        }
    return 0;
    }

 6.一维一边推(美元与马克)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int n;
double f[103][2], x;
int main()
    {
    scanf("%d", &n);
    f[0][0] = 100.00;
    for(int i = 1; i <= n; ++i)
        {
        scanf("%lf", &x);
        x /= 100.00;
        f[i][0] = max(f[i - 1][0], f[i - 1][1] / x);
        f[i][1] = max(f[i - 1][1], f[i - 1][0] * x);
        }
    printf("%.2lf\n", f[n][0]);
    return 0;
    }

 7.一维一边推(乘积最大,注意枚举的顺序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
int n,m;
long long s,a[41][41],f[41][7];
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m>>s;
    for(int i=n;i>=1;i--){
        a[i][i]=s%10;
        s/=10;
    }
    for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--)
            a[j][i]=(a[j][i-1]<<1)+(a[j][i-1]<<3)+a[i][i];
    for(int i=1;i<=n;i++)
        f[i][0]=a[1][i];
    for(int k=1;k<=m;k++)
        for(int i=k+1;i<=n;i++)
            for(int j=k;j<i;j++)
                f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i]);
    cout<<f[n][m]<<endl;
    return 0;
}

8.二维一边推(最大公共子序列,LCS,利用后效性质进行递推)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
char s1[1001], s2[1001];
int l1, l2, f[1001][1001], ans;
int main()
    {
    scanf("%s%s", s1, s2);
    l1 = strlen(s1), l2 = strlen(s2);
    for(int i = 0; i < l1; ++i)
        for(int j = 0; j < l2; ++j)
            {
            if(i)f[i][j] = max(f[i][j], f[i - 1][j]);
            if(j)f[i][j] = max(f[i][j], f[i][j - 1]);
            if(i && j)f[i][j] = max(f[i][j], f[i - 1][j - 1]);
            if(s1[i] == s2[j]) f[i][j] = i && j ? max(f[i][j], f[i - 1][j - 1] + 1) : 1;
            ans = max(ans, f[i][j]);
            }
    printf("%d\n", ans);
    return 0;
    }

 9.二维一边推(LCS拓展题,字符串距离或基因序列配对问题,注意处理前缀先和空格匹配的情况)

1
2
3
4
5
6
7
8
9
10
11
12
13
scanf("%s%s%d", s1 + 1, s2 + 1, &K);
l1 = strlen(s1 + 1);
l2 = strlen(s2 + 1);
for(int i = 1; i <= l1; ++i) f[i][0] = f[i - 1][0] + K;
for(int j = 1; j <= l2; ++j) f[0][j] = f[0][j - 1] + K;
for(int i = 1; i <= l1; ++i)
    for(int j = 1; j <= l2; ++j)
        {
        f[i][j] = f[i - 1][j - 1] + min(abs(s1[i] - s2[j]), K + K);
        f[i][j] = min(f[i][j], f[i - 1][j] + K);
        f[i][j] = min(f[i][j], f[i][j - 1] + K);
        }
printf("%d\n", f[l1][l2]);

10.中链式(能量项链,注意枚举顺序和扩展两倍的处理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int n;
long long h[201], f[201][201];
int main()
    {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &h[i]), h[i + n] = h[i];
    for(int k = 1; k < n; ++k)
        for(int i = 1; i + k <= n + n; ++i)
            for(int j = i + 1; j <= i + k; ++j)
                f[i][i + k] = max(f[i][i + k], f[i][j - 1] + f[j][i + k] + h[i] * h[j] * h[i + k + 1]);
    long long ans = 0;
    for(int i = 1; i <= n; ++i) ans = max(ans, f[i][i + n - 1]);
    printf("%lld\n", ans);
    return 0;
    }

 11.复杂中链式(最大算式,蓝桥杯。。。,注意开long long)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
#include <iostream>
#define oo 0x3f3f3f3f
using namespace std;
int n, K;
long long f[20][20][20];
int main()
    {
    scanf("%d%d", &n, &K);
    for(int i = 1; i <= n; ++i)
        scanf("%lld", &f[i][i][0]);
    for(int m = 0; m <= K; ++m)
        for(int k = m; k < n; ++k)
            for(int i = 1; i + k <= n; ++i)
                for(int j = i; j < i + k; ++j)
                    for(int mm = 0; mm <= m; ++mm)
                        f[i][i + k][m] = max(f[i][i + k][m], max(m ? f[i][j][mm] * f[j + 1][i + k][m - mm - 1] : -oo, f[i][j][mm] + f[j + 1][i + k][m - mm]));
    printf("%lld\n", f[1][n][K]);
    return 0;
    }

 12.非常规动规(筷子,带有贪心思想,可以简化很多状态,还是很好想的,f[i][k]代表前i根筷子组成k双的最小差平方之和)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n, K, t[101], f[101][60];
int main()
    {
    scanf("%d%d", &n, &K);
    for(int i = 1; i <= n; ++i) scanf("%d", &t[i]);
    if(n / 2 < K + 3) puts("-1");
    else{
        memset(f, 127, sizeof f);
        sort(t + 1, t + 1 + n);
        f[0][0] = 0;
        for(int k = 1; k <= K + 3; ++k)
            for(int i = k * 2; i <= n; ++i)
                f[i][k] = min(f[i - 1][k], f[i - 2][k - 1] + (t[i] - t[i - 1]) * (t[i] - t[i - 1]));
        printf("%d\n", f[n][K + 3]);
        }
    return 0;
    }

 13.非常规动规(不重叠线段,要求覆盖线段不重叠时最长的覆盖长度,用f[l]表示覆盖到l为止得到的最大覆盖长,明显是无后效性的,还有算法本身是还有优化空间的,但本题不需要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
struct seg {
    int l, r;
    bool operator < (const seg other) const{ return l < other.l;}
    }s[1001];
int n, f[2001], ans;
int main()
    {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d%d", &s[i].l, &s[i].r);
    sort(s + 1, s + 1 + n);
    for(int i = 1; i <= n; ++i)
        for(int j = 0; j < s[i].l; ++j)
            f[s[i].r] = max(f[s[i].r], f[j] + s[i].r - s[i].l + 1);
    for(int j = 0; j <= 2000; ++j) ans = max(f[j], ans);
    printf("%d\n", ans);
    return 0;
    }

 14.非常规动规(观光游览,连续区间划分问题,注意状态是否已经达到,是否可以用来转移必须用数组标记,具体题目具体分析很重要,很多时候出错就是这种问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
inline void read(int &x)
    {
    int c = getchar(); x = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    }
int m, n, K, c[101][101], f[101][101];
int main()
    {
    read(m), read(n);
    memset(c, 0, sizeof c);
    for(int i = 1; i <= n; ++i)
        {
        int x, y, v;
        read(x), read(y), read(v);
        for(int l = 1; l <= x; ++l)
        for(int r = y; r <= m; ++r)
            c[l][r] += v;
        }
    read(K);
    memset(f, 128, sizeof f);
    f[0][0] = 0;
    for(int j = 1; j <= m; ++j)
    for(int k = 1; k <= K; ++k)
        for(int jj = 0; jj < j; ++jj)
        f[j][k] = max(f[j][k], f[jj][k - 1] + c[jj + 1][j]);
    printf("%d\n", f[m][K]);
    return 0;
    }

 15.火车票(坐火车,注意的要点和上一题一样)(题目传送门)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int L1, L2, L3, C1, C2, C3, n, dis[101], s, t, f[101];
int P(int d)
    {
    if(d <= L1) return C1;
    if(d <= L2) return C2;
    return C3;
    }
int main()
    {
    scanf("%d%d%d%d%d%d%d%d%d", &L1, &L2, &L3, &C1, &C2, &C3, &n, &s, &t);
    for(int i = 2; i <= n; ++i) scanf("%d", &dis[i]);
    if(s > t) swap(s, t);
    memset(f, 126, sizeof f);
    f[s] = 0;
    for(int i = s + 1; i <= t; ++i)
        for(int j = i - 1; j >= s; --j)
            {
            if(dis[i] - dis[j] > L3) break;
            f[i] = min(f[i], f[j] + P(dis[i] - dis[j]));
            }
    printf("%d\n", f[t]);
    return 0;
    }

 

 路径输出

1.三维LCS路径输出,使用递归,其实也可以用循环啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
char s1[101], s2[101], s3[101], path[101];
int l1, l2, l3, f[101][101][101];
int main()
    {
    scanf("%s%s%s", s1 + 1, s2 + 1, s3 + 1);
    l1 = strlen(s1 + 1), l2 = strlen(s2 + 1), l3 = strlen(s3 + 1);
    for(int i = 1; i <= l1; ++i)
        for(int j = 1; j <= l2; ++j)
            for(int k = 1; k <= l3; ++k)
                {
                f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]);
                f[i][j][k] = max(f[i][j][k], f[i][j][k - 1]);
                f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k - 1] + (s1[i] == s2[j] && s2[j] == s3[k]));
                }
    printf("%d\n", f[l1][l2][l3]);
    int i = l1, j = l2, k = l3, l = 0;
    while(f[i][j][k])
        {
        if(f[i][j][k] == f[i][j - 1][k]) --j;
        else if(f[i][j][k] == f[i - 1][j][k]) --i;
        else if(f[i][j][k] == f[i][j][k - 1]) --k;
        else path[++l] = s1[i], --i, --j, --k;
        }
    while(l) putchar(path[l--]);
    return 0;
    }

 优化思想

1.单调队列队头优化(坐电梯,注意枚举顺序是从上到下,点这里进入题目)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>
#include <iostream>
const double oo = 0x3f3f3f3f;
using namespace std;
inline void read(int &x)
    {
    int c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    }
double f[10001], cur[201];
int n, m, l[201], r[201];
inline double calc(double a, double b)
    {return (a*(a + 1) + b*(b + 1)) / 2 / (a + b + 1);}
int main()
    {
    read(n), read(m);
    for(int i = 1; i <= n; ++i) read(l[i]), read(r[i]);
    fill(cur + 1, cur + 1 + n, oo);
    for(int i = 1; i <= n; ++i)
        if(r[i] == m)
            cur[i] = calc(m - l[i], 0);
    for(int j = m - 1; j >= 1; --j)
        {
        f[j] = oo;
        for(int i = 1; i <= n; ++i)
            if(l[i] <= j && j <= r[i])
            f[j] = min(f[j], cur[i]);
        for(int i = 1; i <= n; ++i)
            if(l[i] <= j && j <= r[i])
            cur[i] = min(cur[i], f[j] + calc(r[i] - j, j - l[i]));
        }
    printf("%.5lf\n", f[1] + m - 1);
    return 0;
    }

 

posted @   keshuqi  阅读(655)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示