Living-Dream 系列笔记 第81期
庆祝该系列突破 80 期!!!1
记忆化搜索
dp 的一种 dfs 实现。
P1434
令 \(dp_{i,j}\) 表示以 \((i,j)\) 结束的最长滑坡的长度。
答案:\(\max\{dp_{i,j}\}\)。
初始:\(dp_{i,j}=1\)。
转移:\(dp_{i,j}=dp_{x,y}+1\),其中 \((x,y)\) 为 \((i,j)\) 四个方向上的邻接点。
实现时枚举每个点进行记忆化搜索即可。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=5e2+5; const int dx[]={1,0,-1,0}; const int dy[]={0,1,0,-1}; int r,c; char mp[N][N]; bool vis[N][N]; int get(){ int sum=0; for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) if(mp[i][j]=='0'&&!vis[i][j]) sum++; return sum; } void dfs(int x,int y){ if(x<1||x>r||y<1||y>c||vis[x][y]||mp[x][y]!='0') return; vis[x][y]=1; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; dfs(xx,yy); } } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>r>>c; for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) cin>>mp[i][j]; int ans=1e9; for(int i=1;i<=c;i++){ dfs(1,i); dfs(r,i); } for(int i=1;i<=r;i++){ dfs(i,1); dfs(i,c); } cout<<get(); return 0; }
P1506
从边缘每个点开始 flood-fill 即可,注意边上的点有可能不能作为源点。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=5e2+5; const int dx[]={1,0,-1,0}; const int dy[]={0,1,0,-1}; int r,c; char mp[N][N]; bool vis[N][N]; int get(){ int sum=0; for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) if(mp[i][j]=='0'&&!vis[i][j]) sum++; return sum; } void dfs(int x,int y){ if(x<1||x>r||y<1||y>c||vis[x][y]||mp[x][y]!='0') return; vis[x][y]=1; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; dfs(xx,yy); } } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>r>>c; for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) cin>>mp[i][j]; int ans=1e9; for(int i=1;i<=c;i++){ dfs(1,i); dfs(r,i); } for(int i=1;i<=r;i++){ dfs(i,1); dfs(i,c); } cout<<get(); return 0; }
P4017
令 \(dp_i\) 表示以 \(i\) 结尾的最长食物链的长度。
答案:所有出度为 \(0\) 的点 \(i\) 的 \(\sum dp_i\)。
初始:所有入度为 \(0\) 的点 \(i\) 有 \(dp_i=1\)。
转移:\(dp_i=dp_{cur}+1\)。
注意建反边,为了方便找转移点。
(记搜的题目一般都是从终止节点开始 dfs,因此一般要建反边)
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=5e3+5; const int MOD=80112002; int n,m; int dp[N],in[N],out[N]; vector<int> G[N]; int dfs(int cur){ if(dp[cur]!=0) return dp[cur]%MOD; if(!in[cur]) return dp[cur]=1; for(int i:G[cur]) dp[cur]=(dp[cur]+dfs(i))%MOD; return dp[cur]%MOD; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>m; for(int i=1,u,v;i<=m;i++){ cin>>u>>v; G[v].push_back(u); in[v]++,out[u]++; } int ans=0; for(int i=1;i<=n;i++) if(!out[i]) ans=(ans+dfs(i))%MOD; cout<<ans; return 0; }
P6145
令 \(dp_i\) 表示第 \(i\) 次挤奶的最早时间。
答案:所有 \(dp_i\)。
初始:\(dp_i=s_i\)。
转移:\(dp_i=\max(dp_i,dp_{cur}+w)\)(注意是取 \(\max\),因为要保证所有的邻接点要求均满足,当然必须得满足挤奶时间最晚的那个了)。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+5; int n,m,c; int dp[N]; bool vis[N]; struct Edge{ int v,w; }; vector<Edge> G[N]; int dfs(int cur){ if(vis[cur]) return dp[cur]; vis[cur]=1; for(auto i:G[cur]) dp[cur]=max(dp[cur],dfs(i.v)+i.w); return dp[cur]; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>m>>c; for(int i=1;i<=n;i++) cin>>dp[i]; for(int i=1,u,v,w;i<=c;i++){ cin>>u>>v>>w; G[v].push_back({u,w}); } for(int i=1;i<=n;i++) cout<<dfs(i)<<'\n'; return 0; }
P1775
典。略。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e2+5; int n,m,c; int a[N],dp[N][N],s[N]; bool vis[N][N]; struct Edge{ int v,w; }; vector<Edge> G[N]; int dfs(int l,int r){ if(vis[l][r]) return dp[l][r]; vis[l][r]=1; for(int i=l;i<r;i++) dp[l][r]=min(dp[l][r],dfs(l,i)+dfs(i+1,r)+s[r]-s[l-1]); return dp[l][r]; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n; memset(dp,0x3f,sizeof dp); for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i],dp[i][i]=0; cout<<dfs(1,n); return 0; }
P2217
挺有意思一题。
发现我们关心的东西:当前矩阵是啥,以及分割成了几个矩阵。
当前矩阵需要使用四个坐标描述,因此我们有状态:
令 \(dp_{x,y,xx,yy,k}\) 表示当前矩阵对角顶点为 \((x,y),(xx,yy)\),且已分割了 \(k\) 个矩阵的平方和(\(\sum^n_{i=1} (a_i-avg)^2\))最小值。
值得一提的是,序列 \(a\) 的均方差为:
其中:
然后记忆化搜索即可,转移自己画一下图就能推出来了,注意两部分拼起来的时候不要把平方和再加一遍。
code
#include<bits/stdc++.h> using namespace std; const int N=11; int a,b,n; double avg; int scr[N][N]; double dp[N][N][N][N][N]; double get(int x,int y,int xx,int yy){ int res=0; for(int i=x;i<=xx;i++) for(int j=y;j<=yy;j++) res+=scr[i][j]; return (res-avg)*(res-avg); } double dfs(int x,int y,int xx,int yy,int k){ if(k==1) return dp[x][y][xx][yy][k]=get(x,y,xx,yy); if(dp[x][y][xx][yy][k]) return dp[x][y][xx][yy][k]; dp[x][y][xx][yy][k]=1e9; for(int i=x;i<xx;i++) for(int j=1;j<k;j++) dp[x][y][xx][yy][k]=min(dp[x][y][xx][yy][k],dfs(x,y,i,yy,j)+dfs(i+1,y,xx,yy,k-j)); for(int i=y;i<yy;i++) for(int j=1;j<k;j++) dp[x][y][xx][yy][k]=min(dp[x][y][xx][yy][k],dfs(x,y,xx,i,j)+dfs(x,i+1,xx,yy,k-j)); return dp[x][y][xx][yy][k]; } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>a>>b>>n; for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) cin>>scr[i][j]; for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) avg+=scr[i][j]; avg/=n; cout<<setprecision(2)<<fixed<<sqrt(dfs(1,1,a,b,n)/(double)n); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!