Living-Dream 系列笔记 第36期
T1
经典的 toposort + dp,
可以说所有的按 topo 序的 dp 都是按照此题的套路。
令 \(dp_i\) 表示以 \(i\) 结尾的工作需要的最小时间。
题目明摆着就是要你按 topo 序转移,
于是在 toposort 板子中枚举邻接点的部分进行转移即可。
转移方程:
\[dp_i=\max(dp_j,dp_i+len_j)
\]
(\(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;
}
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号