#状压dp#洛谷 3888 拯救莫莉斯

本文介绍了一个基于状压动态规划的问题解决方法。该方法应用于一个特定矩阵中,通过标记某些单元格来达到最小化权重之和的目标,同时确保所有单元格都被覆盖。文章详细解释了如何利用状态压缩技巧进行优化,并提供了完整的代码示例。

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

题目

有一个n×mn\times mn×m的矩阵,现在标记一些格子,使未标记的格子与标记的相邻,使标记格子的点权和最小,并输出标记格子的个数。


分析

首先我们能够发现n×m≤50且m≤nn \times m \leq 50且m\leq nn×m50mn也就是说m≤7m\leq 7m7,那我们就可以用状压dp实现,设f[n][i][j]f[n][i][j]f[n][i][j]表示第nnn行的标记格子二进制状态为iii,n−1n-1n1行为jjjn−1n-1n1完全覆盖的最小点权和,s[n][i][j]s[n][i][j]s[n][i][j]为最小标记格子数,那么f[n][i][j]=min⁡{f[n−1][j][k]+pay[n][i]},s[n][i][j]=min⁡{s[n−1][j][k]+cnt[i]}f[n][i][j]=\min\{f[n-1][j][k]+pay[n][i]\},s[n][i][j]=\min\{s[n-1][j][k]+cnt[i]\}f[n][i][j]=min{f[n1][j][k]+pay[n][i]},s[n][i][j]=min{s[n1][j][k]+cnt[i]},且满足第n−1n-1n1行完全覆盖,cnt[i]cnt[i]cnt[i]表示二进制数iii的1的个数,pay[n][i]pay[n][i]pay[n][i]表示第nnn行二进制所表示的1的位置的点权和,需要预处理,初始化f[1][i][0]=pay[1][i],s[1][i][0]=cnt[i]f[1][i][0]=pay[1][i],s[1][i][0]=cnt[i]f[1][i][0]=pay[1][i],s[1][i][0]=cnt[i],最后求min⁡{f[n+1][0][i]}\min\{f[n+1][0][i]\}min{f[n+1][0][i]}


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
#define r(i,a,b) for (rr int i=a;i<=b;++i)
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,all,cnt[131],pay[51][131],dp[51][131][131],sum[51][131][131];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
signed main(){
    n=iut(); m=iut(); all=(1<<m)-1;
    r(i,1,all) cnt[i]=cnt[i>>1]+(i&1);
    r(i,1,n){
        r(j,0,m-1) pay[i][1<<j]=iut();
        r(j,1,all) pay[i][j]=pay[i][j-(-j&j)]+pay[i][-j&j];
    }
    memset(dp[1],127/3,sizeof(dp[1]));
    memset(sum[1],127/3,sizeof(sum[1]));
    r(i,0,all) dp[1][i][0]=pay[1][i],sum[1][i][0]=cnt[i];
    r(i,2,n+1){
        memset(dp[i&1],127/3,sizeof(dp[i&1]));
        memset(sum[i&1],127/3,sizeof(sum[i&1]));
        r(now,0,all) r(j,0,all) r(ls,0,all)
        if (((j|now|ls|(j<<1)|(j>>1))&all)==all){
            if (dp[(i&1)^1][j][ls]+pay[i][now]<dp[i&1][now][j]){
          	    dp[i&1][now][j]=dp[(i&1)^1][j][ls]+pay[i][now];
          	    sum[i&1][now][j]=sum[(i&1)^1][j][ls]+cnt[now];
            }
            else if (dp[(i&1)^1][j][ls]+pay[i][now]==dp[i&1][now][j])
                sum[i&1][now][j]=min(sum[i&1][now][j],sum[(i&1)^1][j][ls]+cnt[now]);
        }
    }
    rr int ans1=707406377,ans2=0;
    r(i,0,all){
        if (ans1>dp[(n&1)^1][0][i]) ans1=dp[(n&1)^1][0][i],ans2=sum[(n&1)^1][0][i];
            else if (ans1==dp[(n&1)^1][0][i]) ans2=min(ans2,sum[(n&1)^1][0][i]);
    }
    return !printf("%d %d",ans2,ans1);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值