牛客题单_动态规划课程状压dp习题
牛客题单_动态规划课程状压dp习题
NC14732 锁
大意:
有n名居民, 他们每人有一个重要度。房间的门上可以装若干把锁。假设共有k把锁,命名为1到k。每把锁有一种对应的钥匙,也用1到k表示。钥匙可以复制并发给任意多个居民。每个居民持有若干钥匙,也就是1到k的一个子集。规定一组居民都在场时能打开房门当且仅当他们的重要度加起来至少为m。问至少需要给房间装多少把锁。即,求最小的k,使得可以适当地给居民们每人若干钥匙(即一个1到k的子集),使得任意重要度之和小于m的居民集合持有的钥匙的并集不是1到k,而任意重要度之和大于等于m的居民集合持有的钥匙的并集是1到k。
思路:
当某个集合的重要度的总和没有到达m,但是再加上任意一个人,他们的重要度就能满足要求。
我们假设这个集合缺少一把钥匙,我们求出所有的这样的集合,使得这些集合都缺少一把不同的钥匙,此时所有满足这样条件的集合数量即为所需要的锁的数量了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
LL n, m, dp[1 << 25], a[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
LL num = (1 << n) - 1;
LL res = 0;
for (LL i = 0; i <= num; i++) {
int flag = 1;
for (int j = 0; j < n; j++) {
if(!(i&(1<<j))){
dp[i + (1 << j)] = dp[i] + a[j];
if (dp[i + (1 << j)] < m) flag = 0;
}
}
if(flag&&(dp[i]<m)){
res++;
}
}
cout << res << endl;
return 0;
}
NC15034 德玛西亚万岁
模板题,和acwing327玉米田一样
#include <bits/stdc++.h>
using namespace std;
const int N = 20 + 5;
typedef long long LL;
int n, m;
int mp[N];
LL dp[N][1 << 13];
const LL mod = 100000000;
bool check(int state) {
if ((state >> 1) & state)
return false;
else
return true;
}
int main() {
while (cin >> n >> m) {
memset(dp, 0, sizeof dp);
memset(mp, 0, sizeof mp);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < m; j++) {
int t;
cin >> t;
mp[i] += (!t << j);
}
}
vector<int> st;
st.clear();
for (int i = 0; i < (1 << m); i++)
if (check(i)) {
st.push_back(i);
}
vector<int> state[1 << 13];
for (int i = 0; i < st.size(); ++i) {
state[i].clear();
for (int j = 0; j < st.size(); ++j) {
int a = st[i], b = st[j];
if ((a & b) == 0) state[i].push_back(j);
}
}
dp[0][0] = 1;
for (int i = 1; i <= n + 1; i++) // 第i行
for (int j = 0; j < st.size(); j++) // 第j种合法方案
if (!(st[j] & mp[i]))
for (int k : state[j]) // 第i-1行选择合法方案k
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
cout << dp[n + 1][0] << endl; // 第n+1行选择合法方案0的情况(即000000)
}
return 0;
}
NC16418 宝藏
大意:
给定一个N个点M条边的无向图,要求在图中找出一颗生成树,满足树上的节点到根节点的深度d与该点连到树中的边的权值w之积的和最小。
思路:
方法1:
枚举根节点,然后求以这个点为根的最小值,利用dfs更新方案的最小值即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, m;
LL mp[15][15], dp[1 << 13], dis[15];
void dfs(int state) {
for (int i = 0; i < n; i++) {
if (!(state & (1 << i))) continue;
for (int j = 0; j < n; j++) {
if (state & (1 << j)) continue;
if (mp[i][j] == 0x3f3f3f3f3f3f3f3f) continue;
if(dp[state+(1<<j)]>dp[state]+dis[i]*mp[i][j]){
int pre = dis[j];
dis[j] = dis[i] + 1;
dp[state + (1 << j)] = dp[state] + dis[i] * mp[i][j];
dfs(state + (1 << j));
dis[j] = pre;
}
}
}
}
int main() {
cin >> n >> m;
memset(mp, 0x3f, sizeof mp);
memset(dp, 0x3f, sizeof dp);
while (m--) {
int x, y;
LL w;
cin >> x >> y >> w;
x--, y--;
mp[x][y] = mp[y][x] = min(mp[x][y], w);
}
LL res = 1e18;
for (int i = 0; i < n; i++) {
memset(dp, 0x3f, sizeof dp);
memset(dis, 0, sizeof dis);
dis[i] = 1;
dp[1 << i] = 0;
dfs(1 << i);
res = min(res, dp[(1 << n) - 1]);
}
cout << res << endl;
}
方法2:
dp[i] [j]表示:当前选择点的状态为i,且树的高度等于j的生成树 ,枚举i的所有子集S作为前j - 1层的点,剩余点作为第j层的点,求出第j层的所有点到S的最短边,将这些边权和乘以j,直接加到dp [S] [j - 1]上,即可求出f [i] [j]。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 12, M = 1 << 12, INF = 0x3f3f3f3f;
int n, m;
int d[N][N];
int f[M][N], g[M];
int main() {
scanf("%d%d", &n, &m);
memset(d, 0x3f, sizeof d);
for (int i = 0; i < n; i++) d[i][i] = 0;
while (m--) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
a--, b--;
d[a][b] = d[b][a] = min(d[a][b], c);
}
for (int i = 1; i < 1 << n; i++)
for (int j = 0; j < n; j++)
if (i >> j & 1) {
for (int k = 0; k < n; k++)
if (d[j][k] != INF) g[i] |= 1 << k;
}
memset(f, 0x3f, sizeof f);
for (int i = 0; i < n; i++) f[1 << i][0] = 0;
for (int i = 1; i < 1 << n; i++)
for (int j = (i - 1); j; j = (j - 1) & i)//枚举子集
if ((g[j] & i) == i) {
int remain = i ^ j;
int cost = 0;
for (int k = 0; k < n; k++)
if (remain >> k & 1) {
int t = INF;
for (int u = 0; u < n; u++)
if (j >> u & 1) t = min(t, d[k][u]);
cost += t;
}
for (int k = 1; k < n; k++)
f[i][k] = min(f[i][k], f[j][k - 1] + cost * k);
}
int res = INF;
for (int i = 0; i < n; i++) res = min(res, f[(1 << n) - 1][i]);
printf("%d\n", res);
return 0;
}
NC17061 多彩的树
大意:
有一棵树包含 N 个节点,节点编号从 1 到 N。节点总共有 K 种颜色,颜色编号从 1 到 K。第 i 个节点的颜色为 Ai。
Fi 表示恰好包含 i 种颜色的路径数量。请计算:
(∑Ki=1(Fi×131i))mod(109+7)
思路:
因为颜色只有10种,那么直接状压枚举选择的颜色,然后去dfs找只包含这些颜色的联通块,对于每个联通块,求出这个颜色状态下全部的连通块节点数,把路径数全部累加,并且加上路径数为0的全部只选取一个节点的涂法,即可更新出每个颜色的答案。
最后计算答案的时候,要减去子集的答案,因为被重复记录了
#include <bits/stdc++.h>
#define int long long
using namespace std;
int const N = 2e5 + 10;
int n, m, T;
int a[N], dp[N], vis[N], cnt;
const int mod = 1e9 + 7;
vector<int> mp[N];
int qmi(int a, int k, int p) {
int res = 1 % p; // res记录答案, 模上p是为了防止k为0,p为1的特殊情况
while (k) { // 只要还有剩下位数
if (k & 1)
res = res * a % p; // 判断最后一位是否为1,如果为1就乘上a,模上p,
// 乘法时可能爆int,所以变成long long
k >>= 1; // 右移一位
a = a * a % p; // 当前a等于上一次的a平方,取模,平方时可能爆int,所以变成long
// long
}
return res;
}
void dfs(int now, int fa, int state) {
if (!(state & (1 << a[now]))) return;
if (vis[now]) return;
cnt++;
vis[now] = 1;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i];
if (ne == fa) continue;
dfs(ne, now, state);
}
}
signed main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i],a[i]--;
for (int i = 0; i < n - 1; i++) {
int x, y;
cin >> x >> y;
x--, y--;
mp[x].push_back(y), mp[y].push_back(x);
}
int num = (1 << m) - 1;
for (int i = 0; i <= num; i++) {
memset(vis, 0, sizeof vis);
for (int j = 0; j < n; j++) {
cnt = 0;
if (vis[j]) continue;
dfs(j, -1, i);
dp[i] = (dp[i] + cnt+(cnt * (cnt - 1) / 2) % mod) % mod;
}
}
int res = 0;
for (int i = 0; i <= num; i++) {
for (int j = (i - 1)&i; j; j = (j - 1) & i) {
dp[i] = (dp[i] - dp[j]) % mod;
}
res = (res + (dp[i] * qmi(131, __builtin_popcount(i), mod)) % mod) % mod;
}
cout << (res+mod)%mod;
return 0;
}
NC17890 方格填色
大意:
给一个m x n的方格,Applese想要给方格填上颜色,每个格子可以是黑色或者白色。他要求左右相邻两格不能同为白色且相邻两列不能全为黑色。
求满足条件的方案数。
思路:
矩阵快速幂,贴个AC代码吧,先咕咕咕了
#include <bits/stdc++.h>
#define p 1000000007
using namespace std;
struct statement
{
long long f[32][32];
statement(){memset(f, 0, sizeof(f));}
};
long long maxn;
statement operator*(const statement &a, const statement &b)
{
statement ans;
for (int lmid = 0; lmid <= maxn; lmid++)
for (int rmid = 0; rmid <= maxn; rmid++)
{
if ((lmid != 0 || rmid != 0) && ((lmid & rmid) == 0))
{
for (int l = 0; l <= maxn; l++)
for (int r = 0; r <= maxn; r++)
{
ans.f[l][r] += 1LL * a.f[l][lmid] * b.f[rmid][r] % p;
ans.f[l][r] %= p;
}
}
}
return ans;
}
statement fast_pow(statement x, long long y)
{
statement ans;
for (int i = 0; i <= maxn; i++)
ans.f[i][i] = 1;
while (y > 0)
{
if (y & 1)
ans = ans * x;
x = x * x;
y >>= 1;
}
return ans;
}
int main()
{
long long n, m;
cin >> m >> n;
maxn = (1LL << m) - 1;
statement ans;
for (int i = 0; i <= maxn; i++)
ans.f[i][i] = 1;
ans = fast_pow(ans, n - 1);
long long res = 0;
for (int l = 0; l <= maxn; l++)
for (int r = 0; r <= maxn; r++)
(res += ans.f[l][r]) %= p;
cout << res << endl;
return 0;
}
NC20485 [ZJOI2009]多米诺骨牌
轮廓线DP,先咕咕咕了...
__EOF__
本文链接:https://www.cnblogs.com/dyhaohaoxuexi/p/14487336.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
2020-03-05 VMware虚拟机复制文件卡死的解决
2020-03-05 VMware 克隆一个虚拟机(win7)