ACM:每日学习 状压dp

状压dp:

状压dp是对一般dp的改进:

//对于判断多种物品的取法,开多维数组比较麻烦,也不好开,使用二进制来表示物品的取与否。

//使用二进制的话,位运算就更能省时间了,而且更会节省空空间,敲数组也比较好敲,唯一比较难的就是位运算真是费大脑。一定要熟练的运用位运算,建议看看这个。位运算全面总结,关于位运算看这篇就够了-CSDN博客

状压dp算法的前提:

1.看看是不是有多个要取的数(不一定是多个物品,可能是一个数的每位数要取与否)一定要记录当前下标的状态和上一个节点所取的点,并且记录二进制的点,个数就是2^n(n为要取的物品),所以状压dp大部分是二维数组。

2.再者计算一下时间复杂度,看看n是不是在20左右(因为2^20为100w)要是再有多重循环的话,差不多就10^8了,正好到了计算机1s运算量了。

3.dp的基本特征吧,前面取得的会影响后面要取的。

接下来实战一下,毕竟实战比理论更容易记忆。

例题1:吃奶酪 - 洛谷

这个题目的话是要你求要吃奶酪的最短距离,其中包括n个点这就是多个物品的情况。这个题目的话题意还是比较好理解的,你要做的就是记录上一个点的位置和这个点的位置,循环求这个点到上个点的最短距离,但是你要求的是全局,而不只是单独求一个最短的距离,那么就是dp啦,从多种情况中取得最短的距离,就要最后二进制全部取1之后再循环一遍for(int i;i<=n;i++)求得ans最小。

首先把大体的框架想了一遍然后开始想想如何实现dp了。其实dp就是模板,重要的是二进制的位运算记录。for(int i=0;i<(1<<(n+1));i++)最重要的一步啊。

再者就是要想想如何简便地求距离,我偷学了一招啊,PDD(pair<double,double)(因为是距离,再者点有是实数,得用double,这不是玩梗),用pair存点非常的好用,再随便写个函数来计算就行了。

代码实现:

//洛谷:吃奶酪
//二维的?分别存储奶酪状态和落脚点
//一共有几块dp(2^n-1)
#include <bits/stdc++.h>
using namespace std;
const int N = 17;
#define PDD pair<double, double>
PDD a[N];
double dp[1 << N][N];
// 计算距离
double dis(PDD l, PDD r){
    double dx = l.first - r.first;
    double dy = l.second - r.second;
    return sqrt(dx * dx + dy * dy);
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,i, j, k, g;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i].first >> a[i].second;
    memset(dp, 127, sizeof dp);
    dp[1][0] = 0;
    for (i = 0; i < (1 << (n + 1)); i++){
        if (i & 1)
            for (j = 1; j <= n; j++){
                if (i & (1 << j)){
                    k = i ^ (1 << j);
                    for (g = 0; g <= n; g++)
                        if (k & (1 << g))
                            dp[i][j] = min(dp[i][j], dp[k][g] + dis(a[j], a[g]));
                }
            }
    }
    double ans = 1000000000;
//或者可以改成 db ans=-1,下面写if(ans=-1||ans>dp[(1<<(n+1))-1][i])ans=dp[(1<<(n+1))-1][i];
    for (int i = 1; i <= n; i++){
        ans = min(ans, dp[(1 << (n + 1)) - 1][i]);
    }
    printf("%.2lf\n", ans);
}

接下来我本来想写那个非常经典的题目的,但是由于非常复杂,我还没有完全明白,所以再来个别的例题吧。

例题2:[蓝桥杯 2019 省 A] 糖果 - 洛谷

一看到折磨多的糖果种类和要求的最小值就一定是状压dp啊。

这一题目也是经典啊,对于未给定每包的糖果数量我们可以先将每包糖果压缩为一个二进制的数,再一一地去用for(int j=0;j<(1<<m);j++)来一步步的加糖果袋dp[j|a[i]]就是当前的糖果状态所需要的糖果数量,而dp[(1<<m)-1]是最终2^m-1的全部糖果的状态,此时判断dp数组是否等于设的初值来判断是否能吃到全部的糖果。

代码实现:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=20;
int a[1<<N]={0};
int dp[1<<N];
int n,m,k;//n糖果包数、m糖果种类、k每包糖果的糖果数
signed main(){
    scanf("%d%d%d",&n,&m,&k);
    int num=(1<<m);
    memset(dp,0x3f3f3f3f,sizeof dp);//划分界限
    for(int i=1;i<=n;i++){
        for(int j=1;j<=k;j++){
           int t;
           scanf("%d",&t);
           a[i]=a[i]|(1<<(t-1));//将每包的糖果压缩
        }
        dp[a[i]]=1;
    }
    for(int j=0;j<num;j++)
       for(int i=1;i<=n;i++){
        // dp[j|a[i]]=((dp[j]|a[i])<dp[j]+1)?dp[j|a[i]]:dp[j]+1;
        //条件运算符?:表达式1true结果为表达式2,false结果为表达式3
        dp[j|a[i]]=min(dp[j|a[i]],dp[j]+1);//j|a[i]是用来加糖果的(|同0则0)
       }
    if(dp[num-1]==0x3f3f3f3f)printf("-1");
    else printf("%d\n",dp[num-1]);
}

以上就是比较典型的题目了,还有一种就是给你一个固定的大区域分成几个固定的小区域求分法。这种题型等之后学会在看看能不能发一下。

那就这样吧。~

  • 36
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ggplot.acm 是一个用于实现多重对应分析 (MCA) 的 ggplot2 扩展包。多重对应分析是一种多元数据分析方法,常用于研究因素间的关联和相互作用。 ggplot.acm 提供了一种在 R 语言环境中使用 ggplot2 语法进行 MCA 可视化分析的简单且灵活的工具。 ggplot.acm 提供了一些主要函数和方法,以帮助用户处理和分析多变量数据。用户可以利用这些函数对数据集进行预处理,如数据标准化、缺失值处理等。然后,用户可以使用 ggplot2 语法创建图形,并通过 ggplot.acm 提供的特殊图层函数将 MCA 分析结果添加到图形中。 ggplot.acm 支持在图形中展示多个变量之间的关系和交互作用。用户可以根据需要选择要显示的变量,并使用 ggplot2 的强大图形功能自定义图形样式。此外,ggplot.acm 还提供了一些额外的功能,如通过颜色、形状或尺寸来区分不同组别的变量,以及在图中添加标签和注释等。 ggplot.acm 的设计目标是使 MCA 变得更加容易理解和应用。它提供了一个直观且易于使用的界面,让用户可以通过探索数据和可视化结果来了解变量之间的关系。同时,ggplot.acm 还具有高度可定制性,用户可以根据自己的需求和偏好进行个性化设置。 总而言之,ggplot.acm 是一个能够简化 MCA 分析并提供多样化可视化效果的工具。它使用户能够更轻松地使用 ggplot2 进行多重对应分析,并通过丰富的图形功能更好地理解数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值