Living-Dream 系列笔记 第9期
模拟赛掉大分(悲
T1
板子,不讲。
T2
首先很明显这题是个去重全排列。
和模板的区别就是加了一个 \(sum\) 参数记录目前已经放了几个苹果。
当 \(x=n+1\) 时若 \(sum=m\),则更新答案。
同时加入一个剪枝:若在搜索过程中 \(sum>m\),则直接 return
结束搜索。
#include<bits/stdc++.h> using namespace std; int t,n,m,ans; int tmp[31]; void dfs(int x,int sum){ if(x==n+1){ if(sum==m) ans++; return; } if(sum>m) return; for(int i=tmp[x-1];i<=m;i++){ tmp[x]=i; dfs(x+1,sum+i); } } int main(){ cin>>t; while(t--){ ans=0; cin>>m>>n; dfs(1,0); cout<<ans<<'\n'; } return 0; }
如果你高兴还可以用 dp 做
T3
赛时没想出来,就打了个表跑路,没想到拿了 \(30pts\) /xia
将棋盘看成一个 \(25\) 格的数组,这个题就可以演变为填格子的搜索了。
在 \(\text{DFS}\) 函数中传入两个参数:\(x\) 和 \(tot\),分别记录当前已经填的格子数以及剩下的格子数,初始分别为 \(1,n\)。
若 \(tot=0\),则计算当前矩阵中的五连子个数,存入 \(vis\) 数组中。
否则,循环 \(x \sim 25\),对于每个枚举到的 \(i\),计算出对应的 \(x,y\) 坐标,将此坐标标记为已访问,并且调用 \(\text{dfs}(i+1,tot-1)\) 即可。注意回溯。
#include<bits/stdc++.h> using namespace std; int n,ans; bool vis[31],v[31][31]; void check(){ int sum=0; for(int i=1;i<=5;i++){ for(int j=1;j<=5;j++){ if(!v[i][j]) break; if(j==5) sum++; } for(int j=1;j<=5;j++){ if(!v[j][i]) break; if(j==5) sum++; } } for(int i=1;i<=5;i++){ if(!v[i][i]) break; if(i==5) sum++; } for(int i=1;i<=5;i++){ if(!v[i][6-i]) break; if(i==5) sum++; } vis[sum]=1; } void dfs(int x,int tot){ //cout<<x<<'\n'; if(!tot){ check(); return; } if(25-x+1<tot) return; for(int i=x;i<=25;i++){ int xx=(i-1)/5+1,yy=(i-1)%5+1; v[xx][yy]=1; dfs(i+1,tot-1); v[xx][yy]=0; } } int main(){ cin>>n; dfs(1,n); for(int i=1;i<=12;i++) if(vis[i]) ans+=i; cout<<ans; return 0; }
T4
这题的 \(\text{DFS}\) 做法很好写。将每个公司看作一个格子,分配的机器看作填入的数字,然后按常规的填格子写法即可。
#include<bits/stdc++.h> using namespace std; int n,m,maxx; int ans[31],tmp[31]; int a[31][31]; void dfs(int x,int sum,int tot){ if(x==n+1){ if(tot<=m){ if(sum>maxx){ maxx=sum; for(int i=1;i<=n;i++) ans[i]=tmp[i]; } } return; } for(int i=1;i<=m;i++){ tmp[x]=i; dfs(x+1,sum+a[x][i],tot+i); } } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; dfs(1,0,0); cout<<maxx<<'\n'; for(int i=1;i<=n;i++) cout<<i<<' '<<ans[i]<<'\n'; return 0; }
无奈只有 \(10pts\)(为什么会 WA
两个点啊 \(qwq\))。
于是我们考虑万能的 \(dp\)。
我们令 \(f_{i,j}\) 表示前 \(i\) 个公司分配 \(j\) 个机器的利润总和。
因为分配给子公司的机器数只能为 \(0 \sim j\),则我们在此区间内枚举一个整数 \(k\) 表示给第 \(i\) 个公司分配的机器数,由此得到状态转移方程:
同时,题目要求我们记录分配方案,因此建令 \(p_{i,j,h}\) 表示在前 \(i\) 个公司分配 \(j\) 个机器的最优方案中第 \(h\) 各公司分配到的机器数。
\(p\) 可以在状态转移的时进行更新。更新方程:
#include<bits/stdc++.h> using namespace std; int n,m; int a[31][31],f[31][31],p[31][31][31]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; for(int i=1;i<=n;i++){ for(int j=0;j<=m;j++){ for(int k=0;k<=j;k++){ if(f[i][j]<f[i-1][k]+a[i][j-k]){ f[i][j]=f[i-1][k]+a[i][j-k]; for(int h=1;h<i;h++) p[i][j][h]=p[i-1][k][h]; p[i][j][i]=j-k; } } } } cout<<f[n][m]<<'\n'; for(int h=1;h<=n;h++) cout<<h<<' '<<p[n][m][h]<<'\n'; return 0; }
哈哈没想到吧只有 90 分
由于题目再一次提出了毒瘤的要求:字典序最小。
因此我们考虑枚举整数 \(k\) 表示不给第 \(i\) 个公司的机器数。
所以转移方程就变成了这样:
更新方程变成了这样:
愉快 \(\mathcal{AC}\) !
#include<bits/stdc++.h> using namespace std; int n,m; int a[31][31],f[31][31],p[31][31][31]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; for(int i=1;i<=n;i++){ for(int j=0;j<=m;j++){ for(int k=0;k<=j;k++){ if(f[i][j]<f[i-1][k]+a[i][j-k]){ f[i][j]=f[i-1][k]+a[i][j-k]; for(int h=1;h<i;h++) p[i][j][h]=p[i-1][k][h]; p[i][j][i]=j-k; } } } } cout<<f[n][m]<<'\n'; for(int h=1;h<=n;h++) cout<<h<<' '<<p[n][m][h]<<'\n'; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现