题目
有一个n×mn\times mn×m的矩阵,现在标记一些格子,使未标记的格子与标记的相邻,使标记格子的点权和最小,并输出标记格子的个数。
分析
首先我们能够发现n×m≤50且m≤nn \times m \leq 50且m\leq nn×m≤50且m≤n也就是说m≤7m\leq 7m≤7,那我们就可以用状压dp实现,设f[n][i][j]f[n][i][j]f[n][i][j]表示第nnn行的标记格子二进制状态为iii,n−1n-1n−1行为jjj且前n−1n-1n−1行完全覆盖的最小点权和,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[n−1][j][k]+pay[n][i]},s[n][i][j]=min{s[n−1][j][k]+cnt[i]},且满足第n−1n-1n−1行完全覆盖,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);
}