首页 > 其他分享 >[动态规划] 背包 dp

[动态规划] 背包 dp

时间:2024-05-11 11:42:06浏览次数:25  
标签:辩方 背包 int rint 控方 动态 dp 候选人

背包 dp

AcWing 278. 数字组合

\(n\) 个数就是 $n $ 个物品,每个物品的价值就是它本身的数值,只能用一次,要求价值和为 \(m\) 的方案数。直接 01 背包即可。

int n, m;
int a[N], f[M];

signed main()
{
    cin >> n >> m;
    for (rint i = 1; i <= n; i++) cin >> a[i];
    memset(f, 0, sizeof f);
    f[0] = 1;
    for (rint i = 1; i <= n; i++)
        for (rint j = m; j >= a[i]; j--)
            f[j] += f[j - a[i]];
    cout << f[m] << endl;
    return 0;
}

AcWing 279. 自然数拆分

若干个正整数就是若干个物品,要想能相加为 \(n\),且至少两个数相加,能用上的数就是 \(1\) ~ \(n - 1\),共 \(n - 1\) 个物品,价值就是数值本身。完全背包即可。

int n;
int f[N];

signed main()
{
    cin >> n;
    memset(f, 0, sizeof f);
    f[0] = 1;
    for (rint i = 1; i <= n - 1; i++)
        for (rint j = i; j <= n; j++)
            f[j] = (f[j] + f[j - i]) % mod;

    cout << f[n] << endl;

    return 0;
}

UVA323 Jury Compromise

本题是一个 01 背包问题,我们将 \(n\) 个人看作 \(n\) 个物品,那么每个物品会有三个体积:

    1. 人数,每个候选人都是 \(1\) 个人,最终要选出 \(m\) 个人
    1. 辩方得分,即辩方给每个候选人打的分数 \(a[i]\)
    1. 控方得分,即控方给每个候选人打的分数 \(b[i]\)

因此我们需要依次考虑每个候选人是否选入评审团,当外层循环到阶段 \(i\) 时,表示已经考虑了前 \(i\) 个候选人的入选情况,用 \(bool\) 数组 \(f[j][d][p]\) 表示已有 \(j\) 人被选入评审团,当前辩方总分为 \(d\)、控方总分为 \(p\) 的状态是否可行。

第 \(i\) 个候选人有选和不选两种情况,得出状态转移方程:

\[f[j][d][p] = f[j][d][p] | f[j - 1][d - a[i]][p - b[i]] \]

起始状态 \(f[0][0][0] = 1\),目标是 \(f[m][d][p] = 1\),要求 \(|d - p|\) 尽量小,\(d + p\) 尽量大。

到此我们初步分析出了一个算法,但是并没有很好的利用价值这一维度,我们可以进一步优化,我们可以将每个候选人的辩方、控方双方得分的差 \(a[i] - b[i]\) 作为体积之一,把辩方、控方双方得分的和作为该物品
的价值。

当外层循环到 \(i\) 时,设 \(f[j][k]\) 表示已经在前 \(i\) 个候选人中选出了 \(j\) 个,此时辩方与控方总分的差为 $ k$ 时,辩方与控方总分的和的最大值。

同样有选和不选两种情况,状态转移方程:

\[f[j][k] = max{ f[j][k], f[j - 1][k - (a[i] - b[i])] + (a[i] + b[i]) } \]

起始状态 \(f[0][0] = 0\),目标是 \(f[m][k]\),满足 \(|k|\) 尽量小,当 \(|k|\) 相同时 \(f[m][k]\) 尽量大。

最终还要输出具体方案,用一个 \(d[i][j][k]\) 表示外层循环到 \(i\) 时,状态 \(f[j][k]\) 是从哪个候选人转移过来的。递归找出整个方案即可。

int n, m;
int f[M][K]; 
int d[N][M][K];
//表示 f[i][j][k] 是从哪个候选人转移过来的
int a[N], b[N]; 
//每个候选人的辩方、控方得分
vector<int> path; 
//选择的候选人编号
int suma, sumb; 
//辩方、控方总分

void get_path(int i, int j, int k) 
//从最优状态回推方案
{
    if (!j) return; 
	//回推完所有候选人结束程序
    int last = d[i][j][k];
    get_path(last - 1, j - 1, k - (a[last] - b[last])); 
	//继续递归
    path.push_back(last); 
	//将当前候选人加入方案
    suma += a[last], sumb += b[last]; 
	//累加辩方、控方总分
}

signed main()
{
    int T = 1;
    while (cin >> n >> m && n || m)
    {
        for (rint i = 1; i <= n; i++) cin >> b[i] >> a[i];
        memset(f, -0x3f, sizeof f);
        f[0][400] = 0; //f[0][0] -> f[0][400] (k 平移 400)
        //01背包
        for (rint i = 1; i <= n; i++)
        {
            for (rint j = m; j > 0; j--)
            {
                for (rint k = 0; k <= 800; k++)
                {
                    //不选 i
                    d[i][j][k] = d[i - 1][j][k];

                    //选 i
                    if (k - (a[i] - b[i]) < 0 || k - (a[i] - b[i]) > 800) continue; 
					//状态不合法直接跳过
                    if (f[j][k] < f[j - 1][k - (a[i] - b[i])] + (a[i] + b[i])) 
					//如果辩方、控方总和之和更大,更新
                    {
                        f[j][k] = f[j - 1][k - (a[i] - b[i])] + (a[i] + b[i]); 
						//状态转移
                        d[i][j][k] = i; //记录从哪个候选人转移过来
                    }
                }				
			}
        }

        //找出最优方案对应的 k
        int res = 0;
        for (rint k = 0; k <= 400; k++) // k 尽可能的小
        {
            if (f[m][400 + k] >= 0 && f[m][400 + k] >= f[m][400 - k]) 
			//辩方、控方总分的和尽量大
            {
                res = 400 + k; //选双方总和的和较大的一个 k
                break; //第一个有解的 k 一定最小
            }
            else if (f[m][400 - k] >= 0)
            {
                res = 400 - k;
                break;
            }			
		}

        path.clear(); //清空方案

        suma = sumb = 0; //重置总分
        get_path(n, m, res); //从最优状态回推方案

        //输出
        printf("Jury #%lld\n", T++);
        printf("Best jury has value %d for prosecution and value %lld for defence:\n", sumb, suma);
        for (rint i = 0; i < path.size(); i++) printf(" %lld", path[i]);
        printf("\n\n");
    }
    return 0;
}

AcWing 281. 硬币

本题问的是有几个结果是可以组成的,是一个可行性问题,不是一个最优性问题。可以看出是一个多重背包问题。因此设 \(bool\) 数组 \(f[i][j]\) 表示前 \(i\) 种硬币能否拼成面值 \(j\)

可以把多重背包拆成01背包来做,状态转移方程:

\[f[i][j] = f[i - 1][j] | f[i - 1][j - v[i]] \]

但是这样时间复杂度太高了,需要进行优化,用二进制拆分法。

int n, m;
int a[N]; 
//a[i] 表示第 i 个硬币的面值,s[i] 表示第 i 个硬币的数量
int v[N], cnt; 
//二进制拆分后每个物品的面值
bool f[M]; 
//设 f[i][j] 表示前 i 个硬币能否拼成面值 j,降掉 i 维

signed main()
{
    while (cin >> n >> m, n || m)
    {
        for (rint i = 1; i <= n; i++) cin >> a[i];
        memset(f, 0, sizeof f);
        f[0] = 1;

        //二进制拆分
        cnt = 0;
        for (rint i = 1; i <= n; i++)
        {
            int s;
            cin >> s;
            int k = 1;
            while (k <= s)
            {
                cnt++;
                v[cnt] = a[i] * k;
                s -= k;
                k *= 2;
            }
            if (s > 0)
            {
                cnt++;
                v[cnt] = a[i] * s;
            }
        }

        //01背包
        for (rint i = 1; i <= cnt; i++)
            for (rint j = m; j >= v[i]; j--)
                f[j] |= f[j - v[i]];

        int ans = 0;
        for (rint i = 1; i <= m; i++) ans += f[i];
        cout << ans << endl;
    }
    
    return 0;
}

标签:辩方,背包,int,rint,控方,动态,dp,候选人
From: https://www.cnblogs.com/spaceswalker/p/18186213

相关文章

  • 面向单片机的超轻量级的神经网络推理库+单片机上实现动态加载功能的函数库
    1、TinyMaix-面向单片机的超轻量级的神经网络推理库TinyMaix是专为低资源的单片机设计的AI神经网络推理框架,通常被称为TinyML。TinyMaix可以让你在任意单片机上运行轻量级深度学习模型。TinyMaix的设计原则:易用性>移植性>速度>空间。TinyMaix其实是矽速科技(Sipee......
  • [动态规划] 线性 dp
    线性dpSP15637GNYR04H按照编号从小到大摆放所有人每个人都只能放在已经存在的某个人的后面(除第一行外)任何一行的人数都不能比后一行多状态表示:\(f[a][b][c][d][e]\)表示第一行\(a\)个人,第二行\(b\)个人,...,第五行\(e\)个人的合法方案数然后在每个状态下......
  • mybatisplus 中查询的实体对应的表名是动态的解决方案
    开发中遇到需要查询一些表里的数据,这些数据按照一定的规则存放在不同的数据库表里,例如表名是table_name+月份 table_name_2024_05,table_name_2024_04这样,这些表的结构都相同。网上找了一些动态修改实体对应数据库表名的方法,操作相对复杂而且跟mybatisplus的版本有关。自己......
  • 两个问题类似的dp,二者有时间复杂度的不同
    1.https://vjudge.net/problem/POJ-2229/origin(这个问题是以2的倍数为物品的背包)1#include<iostream>#include<cstdio>#include<fstream>#include<algorithm>#include<cmath>#include<deque>#include<vector>#include<que......
  • keycloak~登录皮肤动态切换的尝试
    keycloak的登录皮肤theme,可以设置领域全局的,或者每个客户端进行单独设置,这种设计是没有问题的,但有时,一个客户端可能有多种主题,这时,你只能再加个客户端,对应新的主题,但这样不方便日后的统计,因为很多统计维度都是以client为基础的,所以,我们需要在进入登录页时,让开发人员转具体的皮肤参......
  • 洛谷题单指南-动态规划3-P1775 石子合并(弱化版)
    原题链接:https://www.luogu.com.cn/problem/P1775题意解读:计算合并石子的最小代价,区间DP。解题思路:状态表示:dp[i][j]表示将第i~j堆石子合并的最小代价,m[i]表示第i堆石子质量,s[i]表示前i堆石子质量前缀和状态转移:考虑最后一次合并,设最后一次合并是将i~k合成的一堆与k+1~j合成......
  • Intel 显卡单机多卡 FSDP 模型 checkpointing 时 Assert Out
    Intel显卡单机多卡FSDP模型checkpointing时AssertOut Intel显卡单机多卡FSDP模型checkpointing时AssertOut现象根因顺藤摸瓜抽丝剥茧解法最后的话现象使用HuggingFaceTrainer在单机多卡环境下对LLAMA2-7B进行LoRAfinetuning时,......
  • Freerdp基本参数说明
    Freerdp基本参数说明语法:xfreerdp[file][options][/v:<server>[:port]] 基础参数: /v:主机IP地址 /u:登录账号 /p:登录密码 /f 全屏显示 /port:3389 端口定义 /version版本查询 /help帮助查询 扩展参数: /bpp:[8/16/24/32] 画面效果和流畅度 /sound:sys:pul......
  • 一道DP(2024ICPC武汉邀请赛A)-shaking trees
    ShakingTrees题外话这题易哥哥跟我说这题的时候,点明了这题是关于高度\(100\)的\(O(n^3)\)或者\(O(n^4)\)的dp,还有提出切割点的概念使序列化。dp是真的,序列化也是真的。只能说易哥哥我的神。不过其实切割点是根据树形态而变的,之前一直以为是不变的,歪了好久。不知道是我没get到......
  • XFreerdp2.x编译安装
    1、下载freerdp编译包gitclonehttps://github.com/FreeRDP/FreeRDP.git或者指定版本zip文件下载 2、安装freerdp所依赖包foriin`find./-typef`;docat${i}|grep-i'openssl-devel';if[$?=="0"];thenecho"${i}";fi;done查看需要的安装包2.x版本的实际......