5.24总结
题目1:B3644 【模板】拓扑排序 / 家谱树
思路
问题本质:将家族成员组织成一个序列,使得每个成员的后代都排在其后面。这是典型的拓扑排序问题。
建模:将每个家族成员看作图中的节点,若成员A是成员B的后代,则添加一条从B指向A的有向边(B→A)。
拓扑排序:
计算每个节点的入度(指向该节点的边数)。
将所有入度为0的节点加入队列。
依次取出队列中的节点,输出该节点,并将其所有邻接节点的入度减1。若邻接节点入度变为0,则加入队列。
重难点:
正确构建图:输入中第i行描述的是i的后代,因此需要添加边i → x。
处理多解:题目要求任意合法序列,因此使用队列实现时自然满足条件。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,in[maxn];
vector<int>vt[maxn];
void top_sort(){
queue<int>q;
for(int i=1;i<=n;i++){
if(in[i]==0){
q.push(i);
}
}
while(!q.empty()){
int x=q.front();
cout<<x<<' ';
q.pop();
for(int v:vt[x]){
in[v]--;
if(in[v]==0){
q.push(v);
}
}
}
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int x;
while(cin>>x){
if(x==0){
break;
}
in[x]++;
vt[i].push_back(x);
}
}
top_sort();
return 0;
}
题目2:D4282 【例4-13】奖金
思路
问题分析:员工奖金需满足约束(a的奖金 > b的奖金),且总奖金最小。每位员工初始奖金为100元。
建模:若a的奖金应高于b,则添加一条从b指向a的有向边(b→a)。
拓扑排序 + 贪心:
初始化所有员工奖金为100元。
计算每个节点的入度。
将所有入度为0的节点加入队列。
处理队列中的节点:遍历其邻接节点,更新邻接节点的奖金为max(当前奖金, 当前节点奖金 + 1),并减少其入度。若入度变为0,则加入队列。
重难点:
多前驱处理:一个节点的奖金需取所有前驱节点奖金+1的最大值。
环检测:若存在环(无法拓扑排序),则输出"Poor Xed"。
最小化总奖金:通过拓扑序动态更新奖金。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,in[maxn],ans[maxn];
vector<int>vt[maxn];
int m;
void top_sort(){
queue<int>q;
for(int i=1;i<=n;i++){
if(in[i]==0){
q.push(i);
}
}
while(!q.empty()){
int x=q.front();
q.pop();
for(int v:vt[x]){
in[v]--;
if(in[v]==0){
q.push(v);
ans[v]=ans[x]+1;
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
ans[i]=100;
}
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
vt[y].push_back(x);
in[x]++;
}
top_sort();
int sum=0;
for(int i=1;i<=n;i++){
sum+=ans[i];
}
cout<<sum;
return 0;
}
题目3:P1807 最长路
思路
问题分析:求DAG中从节点1到节点n的最长路径,可能存在负权边。
动态规划 + 拓扑排序:
初始化dist[1] = 0,其他节点为负无穷(-inf)。
预处理:移除从1不可达的入度为0的节点(避免阻塞拓扑排序)。
拓扑排序:从节点1开始,更新每个邻接节点的最长路径dist[v] = max(dist[v], dist[u] + w)。
重难点:
负权边处理:使用负无穷初始化,避免未更新的节点影响结果。
不可达节点处理:通过relax函数移除无关节点。
输出判断:若dist[n]为负无穷,输出-1;否则输出dist[n]。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,in[maxn],ans[maxn];
struct Node{
int v,val;
};
vector<Node>vt[maxn];
int m;
void relax(){
queue<int>q;
for(int i=2;i<=n;i++){
ans[i]=-inf;
if(in[i]==0){
q.push(i);
}
}
while(!q.empty()){
int x=q.front();
q.pop();
for(Node v:vt[x]){
in[v.v]--;
if(in[v.v]==0){
q.push(v.v);
}
}
}
}
void top_sort(){
queue<int>q;
q.push(1);
relax();
while(!q.empty()){
int x=q.front();
q.pop();
for(Node v:vt[x]){
in[v.v]--;
ans[v.v]=max(ans[v.v],ans[x]+v.val);
if(in[v.v]==0){
q.push(v.v);
}
}
}
if(ans[n]>0){
cout<<ans[n];
}
else{
cout<<-1;
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
vt[x].push_back({y,z});
in[y]++;
}
top_sort();
return 0;
}
本人(KK_SpongeBob)蒟蒻,写不出好文章,但转载请注明原文链接:https://www.cnblogs.com/OIer-QAQ/p/18911012

浙公网安备 33010602011771号