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;
}