Living-Dream 系列笔记 第36期
T1
经典的 toposort + dp,
可以说所有的按 topo 序的 dp 都是按照此题的套路。
令 \(dp_i\) 表示以 \(i\) 结尾的工作需要的最小时间。
题目明摆着就是要你按 topo 序转移,
于是在 toposort 板子中枚举邻接点的部分进行转移即可。
转移方程:
(\(j\) 满足 \(i \to j\))
要取 \(\max\) 是因为要从最晚结束的工作开始。
答案为 \(\max_{i=1}^{n}\{dp_i\}\),时间复杂度 \(O(n+m)\)。
这是递推做法,理所当然的还有一种记忆化做法。
记忆化做法就不用考虑 dp 顺序了,直接枚举邻接点转移即可。
//dp code #include<bits/stdc++.h> using namespace std; int n,ans=-1e9; int w[10031],in[10031]; int dp[10031]; vector<int> G[20031]; void topo(){ queue<int> q; for(int i=1;i<=n;i++) if(!in[i]) dp[i]=w[i],q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); for(auto v:G[u]){ in[v]--,dp[v]=max(dp[v],dp[u]+w[v]); if(!in[v]) q.push(v); } } } int main(){ cin>>n; for(int i=1,u,v;i<=n;i++){ cin>>u>>w[i]; while(cin>>v&&v) G[u].push_back(v),in[v]++; } topo(); for(int i=1;i<=n;i++) ans=max(ans,dp[i]); cout<<ans; return 0; }
//memory search code #include<bits/stdc++.h> using namespace std; int n,ans=-1e9; int w[10031]; int dp[10031]; vector<int> G[20031]; int dfs(int u){ if(dp[u]) return dp[u]; for(auto v:G[u]) dp[u]=max(dp[u],dfs(v)); dp[u]+=w[u]; return dp[u]; } int main(){ cin>>n; for(int i=1,u,v;i<=n;i++){ cin>>u>>w[i]; while(cin>>v&&v) G[u].push_back(v); } for(int i=1;i<=n;i++) ans=max(ans,dfs(i)); cout<<ans; return 0; }
T2
一句话题解:
反向建边 + P1113 + 判环。
#include<bits/stdc++.h> using namespace std; int n,m,ans,f=1; int in[10031]; int dp[10031]; vector<int> G[20031]; void topo(){ queue<int> q; int cnt=0; for(int i=1;i<=n;i++) if(!in[i]) dp[i]=100,q.push(i); while(!q.empty()){ int u=q.front(); q.pop(),cnt++; for(auto v:G[u]){ in[v]--,dp[v]=max(dp[v],dp[u]+1); if(!in[v]) q.push(v); } } if(cnt<n) f=0; } int main(){ cin>>n>>m; for(int i=1,u,v;i<=m;i++){ cin>>u>>v; G[v].push_back(u),in[u]++; } topo(); for(int i=1;i<=n;i++) ans+=dp[i]; if(f) cout<<ans; else cout<<"Poor Xed"; return 0; }
T3
我们发现这个题既有边权,又有点权,于是考虑使用虚拟点 \(\texttt{trick}\)。
建议一个超级源点 \(0\),将 \(0\) 向每个节点连一条边权为 \(S_i\) 的有向边,
并将每条关系 \((a,b,x)\) 中的 \((a,b)\) 间连一条边权为 \(x\) 的有向边。
容易发现这张图是个 DAG,联想到 toposort + dp,然后就变成 P1113 了。
#include<bits/stdc++.h> using namespace std; int n,m,c; int in[100031]; int dp[100031]; struct EdgeInfo{ int v,w; }; vector<EdgeInfo> G[200031]; void topo(){ queue<int> q; for(int i=1;i<=n;i++) if(!in[i]) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); for(auto v:G[u]){ in[v.v]--,dp[v.v]=max(dp[v.v],dp[u]+v.w); if(!in[v.v]) q.push(v.v); } } } int main(){ 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[u].push_back({v,w}),in[v]++; topo(); for(int i=1;i<=n;i++) cout<<dp[i]<<'\n'; return 0; }
习题 T1
一句话题解:
仅对于有摄像头的坐标建边 + B3644 + 记录入队点的个数确定答案。
#include<bits/stdc++.h> using namespace std; int n,ans,maxx=-1e9; int u[131],v[131][131],m[131]; int in[531]; bool vis[531],vis2[531]; vector<int> G[531]; void topo(){ queue<int> q; for(int i=1;i<=n;i++) if(!in[u[i]]) q.push(u[i]); while(!q.empty()){ int cur=q.front(); q.pop(),ans++; for(auto i:G[cur]){ in[i]--; if(!in[i]) q.push(i); } } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>u[i]>>m[i], vis[u[i]]=1,maxx=max(maxx,u[i]); for(int j=1;j<=m[i];j++) cin>>v[i][j]; } for(int i=1;i<=n;i++) for(int j=1;j<=m[i];j++) if(vis[v[i][j]]) G[u[i]].push_back(v[i][j]),in[v[i][j]]++; topo(); if(ans==n) cout<<"YES"; else cout<<n-ans; return 0; }
习题 T2
好题,考察对于 toposort 的深入理解。
若 topo 序唯一,则说明当前关系可以确定顺序,
topo 序唯一时 DAG 呈一条链,
因此直接 dp 判链即可判定 topo 序是否唯一。
(当然更简单的方法是判断每个时刻队列中是否有 \(>1\) 个元素)
否则在读入时每次都做一遍 toposort,若有环就是有矛盾。
否则就是无法确定顺序。
实现时注意矛盾的优先级大于无法确定顺序的优先级。
#include<bits/stdc++.h> using namespace std; int n,m,cnt,sum,k,all; vector<int> G[131]; string ans; int in[131],inn[131]; set<int> s; struct Node{ int to,w; }; void check(int x,int y){ if(x==n) cout<<"Sorted sequence determined after "<<k<<" relations: "<<ans<<".",exit(0); if(y!=all) cout<<"Inconsistency found after "<<k<<" relations.",exit(0); } void topo(){ queue<Node> q; cnt=0,sum=0,ans=""; for(int i='A';i<='Z';i++) if(!inn[i]&&s.count(i)) sum++,q.push({i,1}); while(!q.empty()){ int cur=q.front().to,val=q.front().w; ans+=cur,q.pop(); for(auto i:G[cur]){ inn[i]--; if(!inn[i]) sum++,cnt=max(cnt,val+1),q.push({i,val+1}); } } check(cnt,sum); } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ string t; cin>>t,k=i; G[t[0]].push_back(t[2]); in[t[2]]++,memcpy(inn,in,sizeof(inn)); s.insert(t[0]),s.insert(t[2]),all=s.size(); topo(); } cout<<"Sorted sequence cannot be determined."; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通