【状压DP】CQBZOJ3646 炼金术师

本文探讨了一种利用状态压缩动态规划(状压DP)解决特定类型问题的方法。通过一个具体的例子介绍了如何定义状态,以及如何进行状态转移,特别是对于小规模问题的有效处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:

肥肠水的状压DP题。。。
考虑到k非常的小,可以用dp[i][j][l]dp[i][j][l]表示以ii为首的前k个数中,被访问的状态为j,且最后一步访问到的数是l+il+i
转移也不难。。但的确是需要点技巧的(所以应该算一道实现题?)

首先直接枚举下一步走哪里(需要判断是否合法),以此来转移jjl

然后考虑转移ii

很显然如果l!=0j%2==1

那么可以直接转移到dp[i+1][j>>1][l1]dp[i+1][j>>1][l−1]

否则的话,就有两种可能的转移:
1、转移到[i+1,i+k1][i+1,i+k−1]这个区间中的某个位置
这步直接检查该点是否被访问过,没有的话直接转移即可。

2、转移到[i+k,i+2×k1][i+k,i+2×k−1]这个区间中的某个位置
这一步需要检查是否从ii开始,到i+l的范围内的位置都被访问过,必须都访问过的条件下,才能转移到dp[i+l+1][j>>(l+1)|(1<<(k1))][k1]dp[i+l′+1][j>>(l′+1)|(1<<(k−1))][k−1]

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXK 10
#define MAXN 1010
#define MAXH 5010
#define MAXM 260
#define INF 0x3f3f3f3f
using namespace std;
int dp[MAXN][MAXM][MAXK];
int h[MAXN];
int a[MAXN][MAXN];
int n,k;
int main(){
    SF("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        SF("%d",&h[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            SF("%d",&a[i][j]);
    memset(dp,INF,sizeof dp);
    dp[1][1][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<(1<<k);j++)
            for(int l=0;l<k;l++)
                if(dp[i][j][l]<INF){
                    int maxi=-1;
                    for(int l1=0;l1<k;l1++)
                        if(j&(1<<l1))
                            maxi=l1;
                    for(int l1=0;l1<k;l1++)
                        if((j&(1<<l1))==0&&h[maxi+i]-h[l1+i]<k&&i+l1<=n+1)
                            dp[i][j|(1<<l1)][l1]=min(dp[i][j|(1<<l1)][l1],dp[i][j][l]+a[l1+i][i+l]);
                    if(l!=0&&(j&1)){
                        dp[i+1][j>>1][l-1]=min(dp[i+1][j>>1][l-1],dp[i][j][l]);
                    }
                    else if(l==0){
                        for(int l1=1;l1<k&&i+l1<=n+1;l1++)
                            if((j&(1<<l1))==0)
                                dp[i+1][(j>>1)|(1<<(l1-1))][l1-1]=min(dp[i+1][(j>>1)|(1<<(l1-1))][l1-1],dp[i][j][l]+a[i][i+l1]);
                        for(int l1=k;l1<2*k&&l1+i<=n+1;l1++){
                            if((j&(1<<(l1-k)))==0)
                                break;
                            dp[i+l1-k+1][(j>>(l1-k+1))|(1<<(k-1))][k-1]=min(dp[i+l1-k+1][(j>>(l1-k+1))|(1<<(k-1))][k-1],dp[i][j][l]+a[i][i+l1]);
                        }
                    }
                }
    PF("%d\n",dp[n+1][1][0]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值