题解 P6883 【[COCI2016-2017#3]Kroničan】

· · 题解

我又来刷水题水贡献了

状压DP入门题。。。

先看数据范围 1 \le m \le n \le 20(这里及以下所说的 m 都是题目中的 k) 心生疑惑,再看题显然DP。于是就一定是状压DP。

前置知识:位运算

一、设状态

dp[i] 为将初始状态(即杯子全都有水)变为状态 i 所需最小代价。

其中若将 i 表示成二进制(如 5 表示为 101),第 i 位(这里及以下所说的第几位皆为从右往左)为 1,就表示第 i 个杯子没水,0 就表示第 i 个杯子有水。

二、初始化

初始化 dp 无穷大,dp[0]=0

三、转移方程

dp[i]=\min(dp[i],dp[i \oplus (1<<j)]+c[j+1][k+1])

其中 i 转成二进制后第 j+1 位是 1,第 k+1 位是 0

### 四、计算答案 枚举出所有二进制位是 $0$ 的个数小于等于 $m$ 的状态,从中取最小值,便是答案。 代码: ``` #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=21; int n,m,a[N][N],dp[1<<N]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; memset(dp,0x3f,sizeof dp); dp[0]=0; for(register int i=1;i<(1<<n);i++) for(register int j=0;j<n;j++) if(i&(1<<j))//判断第j+1位是不是1 for(register int k=0;k<n;k++) if(!(i&(1<<k)))//判断第k+1位是不是0 dp[i]=min(dp[i],dp[i^(1<<j)]+a[j+1][k+1]); int ans=-1u/2;//即2的31次方-1 for(register int i=0;i<(1<<n);i++){ int cnt=0; for(register int j=0;j<n;j++) if(!(i&(1<<j)))cnt++;//统计有几位是0 if(cnt<=m)ans=min(ans,dp[i]); } cout<<ans<<endl; return 0; } ``` 点个赞再走吧o( ̄▽ ̄)o