Living-Dream 系列笔记 第36期

Posted on 2024-03-02 16:59  _XOFqwq  阅读(7)  评论(0编辑  收藏  举报

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