版权声明: https://blog.csdn.net/qq_40828060/article/details/82626675
目录
因为它会让你重拾人生方向
(只要别向着图论走去哪都行) __题记
Topsort
P1038 神经网络
题目很水,就是有点坑
不难发现这是个DAG,且个点的更新有严格的顺序要求(因此不能化点为边)
由此想到用topsort边遍历边更新点权
输出层的出度为0,因此只要读入时处理一下即可,不用更新
至于阈值,由于他不在sigma中,因此只要是非输入层直接处理
细节坑点见代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define qwq 0
namespace ljm
{
int dalao;
}
int const maxn=111,maxn2=20110;
int head[maxn],ind[maxn];
int cnt;
int a[maxn],oud[maxn],instack[maxn];
std::queue<int>q;
struct node
{
int to;
int next;
int w;
node(int to=0,int next=0,int w=0):
to(to),next(next),w(w){}
}e[maxn2];
void add(int x,int y,int v)
{
e[++cnt]=(node){y,head[x],v};
head[x]=cnt;
}
int n,m;
void readin()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int U;
scanf("%d%d",&a[i],&U);
if(!a[i])
//直接处理阈值
a[i]-=U;
else
{
q.push(i);
instack[i]=true;
}
}
for(int x,y,z,i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
ind[y]++;
oud[x]++;
}
}
void topsort()
{
while(!q.empty())
{
int u=q.front();
q.pop();
instack[u]=false;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(a[u]>0)
{
//不活跃的点不能传递信息
a[v]+=a[u]*e[i].w;
}
ind[v]--;
if(!ind[v]&&!instack[v])
//instack[]表示队内标记,防止重复入队
{
instack[v]=true;
q.push(v);
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
readin();
topsort();
for(int i=1;i<=n;i++)
{
if(!oud[i]&&a[i]>0)
ljm::dalao=true;
}
if(!ljm::dalao)
{
printf("NULL");
return 0;
}
for(int i=1;i<=n;i++)
if(!oud[i]&&a[i]>0)
//这是个超级大坑点
//输出层(广义上的,指的是出度为0的点)无法传递信息相当于无法输出(就不能算作输出层了)
printf("%d %d\n",i,a[i]);
return 0;
}
P1983 车站分级
感觉做图论越来越有感觉了,很清楚自己写了什么,改了一遍就过了,又立flag!
喜闻乐见的语文题,关键在于建图
选中的车站一定大于没选中的,因此如果在其他车次中要选没选中的,选中的一定会被选,这显然跟topsort序有关
由于要求最少等级,我们可以将<=全看作=
举个例子,i1 i2都连向j,可知i1,i2<=j,此时i1==i2肯定是最优的
因此题目就转化成了求最长的topsort链(就是上题 神经网络的“层数lev”)
建图是个难题,细节见代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<utility>
int const maxn=1011,maxn2=1011;
struct node
{
int to,next;
node(int to=0,int next=0):to(to),next(next){}
}e[maxn*maxn2];
struct node2
{
int nd;
int lev;
node2(int nd=0,int lev=0):
nd(nd),lev(lev){}
};
int a[maxn][maxn2],vis[maxn][maxn2],ind[maxn],cnt,ans,n,m,head[maxn];
int pan[maxn][maxn];
void add(int u,int v)
{
e[++cnt]=(node){v,head[u]};
head[u]=cnt;
}
void readin()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
//共m个车次
scanf("%d",&a[i][0]);
//a[i][0]存第i车次的站数
for(int j=1;j<=a[i][0];j++)
{
scanf("%d",&a[i][j]);
//a[i][j]表示第i车次的第j站
vis[i][a[i][j]]=true;
}
for(int j=a[i][1];j<=a[i][a[i][0]];j++)
//搜索第i车次起点到终点的所有站点
{
if(vis[i][j])
//只找未被选中的站点
continue;
for(int k=1;k<=a[i][0];k++)
//找选中的站点
//令j向k连边
{
int v=a[i][k];
if(!pan[j][v])
{
add(j,v);
ind[v]++;
pan[j][v]=true;
}
}
}
}
}
void topsort()
{
std::queue<node2>q;
for(int i=1;i<=n;i++)
if(!ind[i])
q.push(node2(i,1));
// inque[i]=true;
while(!q.empty())
{
node2 u=q.front();
q.pop();
// inque[i]=false;
for(int i=head[u.nd];i!=-1;i=e[i].next)
{
int v=e[i].to;
ind[v]--;
if(!ind[v])
{
q.push(node2(v,u.lev+1));
ans=std::max(ans,u.lev+1);
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
readin();
topsort();
printf("%d",ans);
return 0;
}
最小生成树
Prim模板
用前向星存边
把每个点扩展到的边压成点放进堆里
根据优先队列的性质,就能求出集合内到集合外的最小距离
正是因为这个,我们就不用for1-n来求last了…
如果已经在集合内,直接continue,因此无脑压入队列即可
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
int const maxn=5100,maxn2=200100;
int cost[maxn],vis[maxn],cnt,sum,head[maxn],n,m,num;
struct E
{
int to,next,w;
E(int to=0,int next=0,int w=0):
to(to),next(next),w(w){}
}e[maxn2<<1];
struct node
{
int nd,dis;
bool operator<(const node &b)const
{
return dis>b.dis;
}
node(int nd=0,int dis=0):
nd(nd),dis(dis){}
};
void add(int x,int y,int w)
{
e[++cnt]=(E){y,head[x],w};
head[x]=cnt;
}
void readin()
{
scanf("%d%d",&n,&m);
for(int u,v,w,i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
}
void prim()
{
std::priority_queue<node>q;
q.push(node(1,0));
cost[1]=0;
while(!q.empty())
{
int u=q.top().nd;
int val=q.top().dis;
q.pop();
if(vis[u])
continue;
num++;
sum+=val;
vis[u]=true;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(e[i].w<cost[v])
//cost[i]表示从集合内到i的最短距离
{
cost[v]=e[i].w;
q.push(node(v,e[i].w));
}
}
}
}
void write()
{
if(num<0)
{
printf("orz");
return;
}
printf("%d",sum);
}
int main()
{
memset(head,-1,sizeof(head));
memset(cost,0x7f7f7f7f,sizeof(cost));
readin();
prim();
write();
return 0;
}