暑假集训 || 动态规划

Posted pinkglightning

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假集训 || 动态规划相关的知识,希望对你有一定的参考价值。

·背包问题:

有很多的变式

HDU 2546
即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)
首先拿出5元买最贵的东西,那接下来就是背包容量m-5,物品数量 n-1 的01背包问题了
技术分享图片
if(m<5)    cout<<m<<endl;
else
{
    int f[1010];//f[j]表示买前i件物品,预算为j时的最大花销
    memset(f,0,sizeof(f));
    for(int i=1; i<=n-1; i++)
        for(int j=m-5; j>=price[i]; j--)
            if(f[j]<f[j-price[i]]+price[i])
                f[j]=f[j-price[i]]+price[i];
    cout<<m-max-f[m-5]<<endl;
}
View Code

 

CodeForces 544C
n个程序员要写m行程序,每个程序员在写一行程序时会产生ai个bug,写完m行共产生<=b个bug共有多少种方法
完全背包问题f[i][j][k]表示第i个程序员写了j行产生k个bug的方法时
f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k-a[i]] (考虑这个程序员这一行写或者不写)
技术分享图片
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int f[550][550], a[550];
int main()
{
    int n, m, b, mod;
    scanf("%d %d %d %d", &n, &m, &b, &mod);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    f[0][0] = 1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            for(int k = a[i]; k <= b; k++)
                f[j][k] = (f[j][k] + f[j-1][k-a[i]]) % mod;
    int ans = 0;
    for(int i = 0; i <= b; i++) ans = (ans + f[m][i]) % mod;
    printf("%d
", ans);
    return 0;
}
View Code

 

POJ 1837
一个臂长15的天平,在不同位置有钩子,钩子上可以挂砝码,给你G个砝码,要把所有砝码都挂在钩子上,一共有多少种挂法
f[i][j]表示挂了i个砝码,天平状态为j时的情况数,j=7500时表示平衡,j<7500向左倾斜,j>7500向右倾斜
技术分享图片
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
#define SZ 20100
int c[33], g[33];
int f[33][SZ];
int main()
{
    int C, G;
    scanf("%d %d", &C, &G);
    for(int i = 0; i < C; i++) scanf("%d", &c[i]);
    for(int i = 1; i <= G; i++) scanf("%d", &g[i]);
    memset(f, 0, sizeof(f));
    for(int i = 0; i < C; i++) f[1][7500 + g[1]*c[i]] = 1;
    int ans = 0;
    for(int i = 2; i <= G; i++)
        for(int j = 0; j < 15000; j++)
            for(int k = 0; k < C; k++)
                if(f[i-1][j] != 0)
                    f[i][j + g[i]*c[k]] += f[i-1][j];
    printf("%d
", f[G][7500]);
    return 0;
}
View Code

 

HDU 4901
题意:一个序列,取两个集合A和B,使得A集合中所有元素在B集合的左边,并且A集合中所有元素的异或值等于B集合中所有元素的and值,求满足条件的集合数量
f[i][j]为前i个数异或值为j的方案数。g[i][j]为后i个数and值为j的方案数
考虑到在中间的分割点可能重复选择,dp[i][j]表示后i个数且第i个数一定被选择的方案数
然后乘法原理
技术分享图片
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
#define SZ 1010
const LL mod = 1e9+7;
LL f[SZ][1030], g[SZ][1030], dp[SZ][1030];
int a[SZ];
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= 1024; j++)
                f[i][j] = g[i][j] = dp[i][j] = 0;
        for(int i = 1; i < n; i++)
        {
            f[i][a[i]]++;
            for(int j = 0; j <= 1024; j++)
                if(f[i-1][j])
                {
                    f[i][j^a[i]] = (f[i][j^a[i]] + f[i-1][j]) % mod;
                    f[i][j] = (f[i][j] + f[i-1][j]) % mod;
                }
        }
        for(int i = n; i > 1; i--)
        {
            g[i][a[i]]++;
            dp[i][a[i]]++;
            for(int j = 0; j <= 1024; j++)
                if(g[i+1][j])//到i+1位时的方案数,i+1位可取可不取
                {
                    g[i][j] = (g[i][j] + g[i+1][j]) % mod;//到i位时的方案数,i位不取
                    g[i][j&a[i]] = (g[i][j&a[i]] + g[i+1][j]) % mod;//到i位时的方案数,i位取
                    dp[i][j&a[i]] = (dp[i][j&a[i]] + g[i+1][j]) % mod;//到i位时的方案数,i位必须取
                }
        }
        LL ans = 0;
        for(int i = 1; i < n; i++)
            for(int j = 0; j <= 1024; j++)
                ans = (ans + f[i][j] * dp[i+1][j]) % mod;
        printf("%lld
", ans);
    }
    return 0;
}
View Code

 

以上是关于暑假集训 || 动态规划的主要内容,如果未能解决你的问题,请参考以下文章

2022.07.13 暑假集训 个人排位赛

2022.07.13 暑假集训 个人排位赛

2022.07.08 暑假集训 个人排位赛

2022.07.08 暑假集训 个人排位赛

2022.07.08 暑假集训 个人排位赛

2022.07.06 暑假集训 个人排位赛