【笔记】拓扑排序(Ⅰ)
题单
拓扑排序:将一个有向无环图
简单食用方法:
0X00 B3644 家谱树
B题库来的,所以显然是板。
入度为
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,x,in[105];
vector<int> a[105];
queue<int> q;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
while(x){
in[x]++,a[i].push_back(x);
scanf("%d",&x);
}
}
for(int i=1;i<=n;i++){
if(!in[i]) q.push(i),printf("%d ",i);
}
while(q.size()){
int k=q.front();
q.pop();
for(int i=0;i<a[k].size();i++){
int tmp=a[k][i];
in[tmp]--;
if(!in[tmp]){
q.push(tmp);
printf("%d ",tmp);
}
}
}
return 0;
}
0X01 P1807 最长路
因为要存下一个点和路径长度,所以用结构体。
开始时将所有入度为
每次找点时更新到这个点的最大距离,将 原来最大距离 与 到通向它的点的最大距离
ans[tmp.to]=max(ans[tmp.to],ans[k]+tmp.dis);
同时将其入度减一(懒惰删点),如果入度为
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,in[1505],ans[1505],x,y,w;
bool vis[1505];
struct node{
int to,dis;
}k;
vector<node> a[1505];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
in[y]++;
k.to=y,k.dis=w;
a[x].push_back(k);
}
memset(ans,-0x3f,sizeof(ans));
ans[1]=0;
for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
while(q.size()){
int k=q.front();
q.pop();
for(int i=0;i<a[k].size();i++){
node tmp=a[k][i];
vis[tmp.to]=1;
ans[tmp.to]=max(ans[tmp.to],ans[k]+tmp.dis);
in[tmp.to]--;
if(!in[tmp.to]) q.push(tmp.to);
}
}
if(!vis[n]) printf("-1");
else printf("%d",ans[n]);
return 0;
}
0X02 P4017 最大食物链计数
因为统计最大食物链,所以记录入度的同时出度也应该记录。最后答案是所有出度为
同样,每次找到一个点就更新它的答案(加上 到达通向它的那个点的食物链数):
ans[tmp]=(ans[tmp]+ans[k])%mod;
Code:
#include<bits/stdc++.h>
#define mod 80112002
using namespace std;
int n,m,in[5005],out[5005],x,y,ans[5005],Ans;
vector<int> a[5005];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
out[x]++,in[y]++;
a[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(!in[i]){
ans[i]=1;
q.push(i);
}
}
while(q.size()){
int k=q.front();
q.pop();
for(int i=0;i<a[k].size();i++){
int tmp=a[k][i];
ans[tmp]=(ans[tmp]+ans[k])%mod;
in[tmp]--;
if(!in[tmp]) q.push(tmp);
}
}
for(int i=1;i<=n;i++){
if(!out[i]) Ans+=ans[i],Ans%=mod;
}
printf("%d",Ans);
return 0;
}
0X03 P6145 [USACO20FEB]Timeline G
巧妙地将日期问题转化为图上问题。
第
所有可以将初始答案设为
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,c,ans[100005],in[100005],x,y,w;
struct node{
int to,val;
}k;
vector<node> a[100005];
queue<int> q;
int main(){
scanf("%d%d%d",&n,&m,&c);
for(int i=1;i<=n;i++) scanf("%d",&ans[i]);
for(int i=1;i<=c;i++){
scanf("%d%d%d",&x,&y,&w);
in[y]++;
k.to=y,k.val=w;
a[x].push_back(k);
}
for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
while(q.size()){
int tt=q.front();
q.pop();
for(int i=0;i<a[tt].size();i++){
node tmp=a[tt][i];
ans[tmp.to]=max(ans[tmp.to],ans[tt]+tmp.val);
in[tmp.to]--;
if(!in[tmp.to]) q.push(tmp.to);
}
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
0X04 P2419 [USACO08JAN]Cow Contest S
看其他人的全是
首先想到,设一个人已经明确的比他强的人数为
那么如何算
对于
最后统计时,
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,in1[105],in2[105],x,y,ans;
bool m1[105][105],m2[105][105];
vector<int> bet[105],wos[105];
queue<int> q1,q2;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
in1[y]++,in2[x]++;
bet[y].push_back(x),wos[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(!in1[i]) q1.push(i);
if(!in2[i]) q2.push(i);
}
while(q1.size()){
int k=q1.front();
q1.pop();
for(int i=0;i<wos[k].size();i++){
int tmp=wos[k][i];
m1[tmp][k]=1;
for(int j=1;j<=n;j++) if(m1[k][j]) m1[tmp][j]=1;
in1[tmp]--;
if(!in1[tmp]) q1.push(tmp);
}
}
while(q2.size()){
int k=q2.front();
q2.pop();
for(int i=0;i<bet[k].size();i++){
int tmp=bet[k][i];
m2[tmp][k]=1;
for(int j=1;j<=n;j++) if(m2[k][j]) m2[tmp][j]=1;
in2[tmp]--;
if(!in2[tmp]) q2.push(tmp);
}
}
for(int i=1;i<=n;i++){
int cnt=0;
for(int j=1;j<=n;j++) if(m1[i][j]||m2[i][j]) cnt++;
if(cnt==n-1) ans++;
}
printf("%d",ans);
return 0;
}
0X05 UVA1423 Guess
是正号就建正向边,负号就建反向边。然后跑拓扑就可以通过每次
最后前缀和数组两两作差即可求出一组符合条件的答案。
注意每次要清空图,同时为前缀和数组赋初值。
Code:
#include<bits/stdc++.h>
using namespace std;
int n,in[15],T,sum[15];
char c;
vector<int> a[15];
queue<int> q;
void solve(){
for(int i=0;i<=10;i++) a[i].clear(),sum[i]=20;
scanf("%d",&n);
for(int i=0;i<n;i++){
for(int j=i+1;j<=n;j++){
cin>>c;
if(c=='+') a[j].push_back(i),in[i]++;
if(c=='-') a[i].push_back(j),in[j]++;
}
}
for(int i=0;i<=n;i++) if(!in[i]) q.push(i);
while(q.size()){
int k=q.front();q.pop();
for(int i=0;i<a[k].size();i++){
int tmp=a[k][i];
sum[tmp]=sum[k]-1;
in[tmp]--;
if(!in[tmp]) q.push(tmp);
}
}
for(int i=1;i<=n;i++) printf("%d ",sum[i]-sum[i-1]);
printf("\n");
}
int main(){
scanf("%d",&T);
while(T--) solve();
return 0;
}
0X06 CF919D Substring
将
考虑转移,对于二十六个字母(
- 若是当前节点,则
- 否则
其中
然后考虑如何判环。例如以下情况:
红框中显然是环。模拟其处理过程。第一次删掉了入度为
然后会发现,这时没有入度为
而对于没有环的情况,所有点都是会被遍历到的。
所以,用
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,b[300005],in[300005],f[300005][26];
int ans,cnt,x,y;
string s;
vector<int> a[300005];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
cin>>s;
for(int i=1;i<=n;i++) b[i]=s[i-1]-'a',f[i][b[i]]++;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
in[y]++;
a[x].push_back(y);
}
for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
while(q.size()){
int k=q.front();
q.pop();
cnt++;
for(int i=0;i<a[k].size();i++){
int tmp=a[k][i];
for(int j=0;j<26;j++){
if(b[tmp]==j) f[tmp][j]=max(f[tmp][j],f[k][j]+1);
else f[tmp][j]=max(f[tmp][j],f[k][j]);
}
in[tmp]--;
if(!in[tmp]) q.push(tmp);
}
}
if(cnt<n) printf("-1");
else{
for(int i=1;i<=n;i++){
for(int j=0;j<26;j++) ans=max(ans,f[i][j]);
}
printf("%d",ans);
}
return 0;
}
0X07 P1038 [NOIP2003 提高组] 神经网络
题目没有说清楚,输入层神经元不需要减去阈值
输入
然后就是拓扑。注意,当一个点的
最后的答案是
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,in[105],out[105],c[105],u,x,y,w,fl;
struct node{
int to,v;
}k;
vector<node> a[105];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&c[i],&u);
if(c[i]) q.push(i);
else c[i]-=u;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
k.to=y,k.v=w;
in[y]++,out[x]++;
a[x].push_back(k);
}
while(q.size()){
int t=q.front();
q.pop();
if(c[t]<=0) continue;
for(int i=0;i<a[t].size();i++){
node tmp=a[t][i];
c[tmp.to]+=tmp.v*c[t];
in[tmp.to]--;
if(!in[tmp.to]) q.push(tmp.to);
}
}
for(int i=1;i<=n;i++){
if(c[i]>0&&!out[i]) printf("%d %d\n",i,c[i]),fl=1;
}
if(!fl) printf("NULL");
return 0;
}
0X08 P1137 旅行计划
和“最大食物链计数”基本相同,只要把转移改一下即可(只记到达的数量):
ans[tmp]=max(ans[tmp],ans[k]+1);
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,in[100005],x,y,ans[100005];
vector<int> a[100005];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
in[y]++;
a[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(!in[i]){
ans[i]=1;
q.push(i);
}
}
while(q.size()){
int k=q.front();
q.pop();
for(int i=0;i<a[k].size();i++){
int tmp=a[k][i];
ans[tmp]=max(ans[tmp],ans[k]+1);
in[tmp]--;
if(!in[tmp]) q.push(tmp);
}
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)