题解:P12282 [蓝桥杯 2024 国 Python A] 羊圈

· · 题解

发现 nm 都很小,尤其是 n 最大只有 15,暴力自然是可以枚举栅栏的全排列,然后 01dfs 每个位置是否放栅栏。

数据范围小,且可以转化为全排列问题,所以考虑状压 dp。

根据暴力,我们需要知道当前栅栏的使用情况和当前位置(确定放栅栏后能框住的范围),所以设 f_{i,S} 表示当前位置为 i,栅栏使用状态为 S 时,跑掉的羊的数量的最小期望。

想了一会转移方程,发现直接求最小逃跑期望不大好求,考虑正难则反,设 f_{i,S} 表示最大不逃跑期望。

转移方程:

答案即为所有的 $\min\{\sum\limits_{k=1}^{n} p_k-f_{m,S}\}$。 注意特判如果所有羊圈能完全覆盖所有羊,则直接输出 $0$。 ```cpp #include <bits/stdc++.h> using namespace std; int l[15], tot; double p[205]; double dp[205][1 << 15], sum[205]; int main(){ int n, m; cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> l[i], tot += l[i]; for(int i = 1; i <= m; i ++) cin >> p[i]; if(tot >= m){ // 特判 cout << "0.00"; return 0; } // 前缀和 for(int i = 1; i <= m; i ++){ sum[i] = sum[i - 1] + p[i]; } // 初始化dp for(int i = 0; i <= m; i ++){ for(int s = 0; s <(1 << n); s ++){ dp[i][s] = -1e18; } } dp[0][0] = 0; for(int i = 1; i <= m; i ++){ for(int s = 0; s < (1 << n); s ++){ // 情况1:当前位置不放任何羊圈 dp[i][s] = dp[i - 1][s]; // 情况2:尝试在当前位置放置一个羊圈 for(int j = 1; j <= n; j ++){ if((s & (1 << j-1)) && i >= l[j]){ dp[i][s] = max(dp[i][s], dp[i - l[j]][s ^ (1 << j-1)] + sum[i] - sum[i - l[j]]); } } } } double minn = 1e18; for(int s = 0; s < (1 << n); s ++){ minn = min(minn, sum[m] - dp[m][s]); } if(minn < 1e-8) minn = 0; // 避免精度误差 cout << fixed << setprecision(2) << minn; return 0; } ```