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

代码实现:

//洛谷:吃奶酪
//二维的?分别存储奶酪状态和落脚点
//一共有几块dp(2^n-1)
#include 
using namespace std;
const int N = 17;
#define PDD pair
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<

代码实现:

#include
using namespace std;
#define int long long 
const int N=20;
int a[1<

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

那就这样吧。~

ACM:每日学习 状压dp_第1张图片

你可能感兴趣的:(ACM2024寒假集训,dp,学习,c++,算法,动态规划)