int G[N][N];
int path[N][N];//path[i][j]=x表示i到j的路径上除i外的第一个点是x
void init(int n) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j)
G[i][j] = 0;
else
G[i][j] = INF;
path[i][j] = j;
}
}
}
void floyd(int n) {
for (int k = 1; k <= n; k++) { //枚举中间点
for (int i = 1; i <= n; i++) { //枚举起点
for (int j = 1; j <= n; j++) { //枚举终点
if (G[i][k] < INF && G[k][j] < INF) {
if (G[i][j] > G[i][k] + G[k][j]) { //松弛操作
G[i][j] = G[i][k] + G[k][j];
path[i][j] = path[i][k]; //更新路径
}
else if (
G[i][j] == G[i][k] + G[k][j] && path[i][j] > path[i][k]) { //在最短路相同的情况下,更新字典序最小的路径
path[i][j] = path[i][k]; //最终path中存的是字典序最小的路径
}
}
}
}
}
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
init(n);
for (int i = 1; i <= m; i++) {
int x, y, dis;
scanf("%d%d%d", &x, &y, &dis);
//无向图添边一次,有向图添边两次
G[x][y] = dis;
G[y][x] = dis;
}
floyd(n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
printf("%d ", G[i][j]);
printf("\n");
}
}
return 0;
}
Floyd 算法又称为插点法,是一种用于寻找给定的加权图中多源点之间最短路径的算法。
其最大特点是可以计算出现负边权时的最短路,实际应用中,很多题目不是问如何用 Floyd 求最短路,而是用 Floyd 的动态规划思想来解决类似 Floyd 的问题。
其时间复杂度是 O(NNN),N是顶点数。
int dis[N];//单源最短距离
int G[N][N];//G[i][j]表示i到j的有向边长
bool vis[N];//表示w[i]是否已经计算完
void dijkstra(int n,int s){
for(int i=1;i<=n;i++){
int x;//x标记当前最短w的点
int min_dis=INF;//记录当前最小距离
for(int y=1;y<=n;y++){
if(!vis[y] && min_dis>=dis[y]){
x=y;
min_dis=dis[x];
}
}
vis[x]=true;
for(int y=1;y<=n;y++)
dis[y]=min(dis[y],dis[x]+G[x][y]);
}
}
int main(){
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,dis;
scanf("%d%d%d",&x,&y,&dis);
G[x][y] = G[y][x] = dis; //无向图添边一次,有向图添边两次
}
int start;
scanf("%d",&start);
dijkstra(n,start);
for(int i=1;i<=n;i++)
printf("%d\n",dis[i]);
return 0;
}
int n,m;
struct Edge{//边
int from;//边的起点
int to;//边的终点
int dis;//边的长度
Edge(int f,int t,int d){//构造函数
from=f;
to=t;
dis=d;
}
};
struct HeapNode{//Dijkstra用到的优先队列的结点
int dis;//点到起点距离
int u;//点的序号
HeapNode(int a,int b){
dis=a;
u=b;
}
bool operator < (const HeapNode &rhs) const {
return dis > rhs.dis;
}
};
struct Dijkstra{
int n,m;//点数、边数
vector<Edge> edges;//边列表
vector<int> G[N];//每个结点出发的边的编号
bool vis[N];//是否走过
int dis[N];//起点s到各点的距离
int p[N];//从起点s到i的最短路中的最后一条边的编号
void init(int n) {//初始化
this->n = n;
for(int i=0;i<n;i++) //清空邻接表
G[i].clear();
edges.clear();//清空边列表
}
void AddEdge(int from,int to,int diss) {//添加边,若为无向图,调用两次
edges.push_back(Edge(from,to,diss));
m=edges.size();//边的个数
G[from].push_back(m-1);//添加至边列表
}
int dijkstra(int s) {//求s到所有点的距离
memset(dis,INF,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[s]=0;
priority_queue<HeapNode> Q;//优先队列
Q.push(HeapNode(0,s));
while(!Q.empty()) {
HeapNode x=Q.top();
Q.pop();
int u=x.u;
if(vis[u])//若已被访问
continue;
vis[u]=true;//标记为已访问
for(int i=0;i<G[u].size();i++) {//枚举所有与当前点相连的边
Edge &e=edges[G[u][i]];
if(dis[e.to] > dis[u]+e.dis) {//更新距离
dis[e.to] = dis[u]+e.dis;
p[e.to]=G[u][i];
Q.push(HeapNode(dis[e.to],e.to));
}
}
}
return dis[n];//返回起点s到终点n最短距离
}
}DJ;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
{
DJ.init(n);//初始化
for(int i=0;i<m;i++) {
int x,y,dis;
scanf("%d%d%d",&x,&y,&dis);
//无向图添边两次
DJ.AddEdge(x,y,dis);
DJ.AddEdge(y,x,dis);
}
int start;
scanf("%d",&start);
DJ.dijkstra(start);//求start到各点的距离
for(int i=0,j=0,s=++start;i<n;i++)//输出start到各点的距离
printf("%d->%d: %d\n",s,++j,DJ.dis[i]);
}
return 0;
}
Dijkstra 算法的思想,就是一开始将起点到终点的距离标记为 0,而后进行 n 次循环,每次找出一个到起点距离 dis[u] 最短的点 u ,将它从蓝点变为白点,随后枚举所有白点 Vi,如果以此白点为中转到达蓝点 Vi 的路径 dis[u]+w[u][vi] 更短的话,这将它作为 Vi 的更短路径(此时还不能确定是不是Vi的最短路径)。
Dijkstra 算法是单源最短路径算法,即计算起点只有一个的情况到其他点的最短路径,其无法处理存在负边权的情况。
其时间复杂度是:O(E+VlogV)
SPFA
Bellman-Ford算法适用于计算单源最短路径,即:只能计算起点只有一个的情况。
其最大特点是可以处理存在负边权的情况,但无法处理存在负权回路的情况。
其时间复杂度为:O(V*E),其中,V 是顶点数,E 是边数。
【算法分析】
Bellman Ford 算法与 Dijkstra 算法的思想相同,只不过 Dijkstra 是每次确定一个最短距离点,并用这个点去更新与之相连的其他边,而 Ford 算法是每次更新所有的边,从而确定一个点的最短距离
————————————————
int dis[N];
bool vis[N];
void SPFA(int S) {
memset(vis, false, sizeof(vis));
memset(dis, INF, sizeof(dis));
dis[S] = 0;
queue<int> Q;
Q.push(S);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = false;
for (int i = head[x]; i != -1; i = edge[i].next) {
int to = edge[i].to;
if (dis[to] > dis[x] + edge[i].dis) {
dis[to] = dis[x] + edge[i].dis;
if (!vis[to]) {
vis[to] = true;
Q.push(to);
}
}
}
}
}
优化spfa
struct Edge{
int to,dis;
};
vector<Edge> edge[N];
bool vis[N];
int dis[N];
void SPFA(int s) {
memset(dis, INF, sizeof(dis));
memset(vis, false, sizeof(vis));
vis[s] = true;
dis[s] = 0;
deque<int> Q;
Q.push_back(s);
while (!Q.empty()) {
int x = Q.front();
Q.pop_front();
vis[x] = 0;
for (int i = 0; i < edge[x].size(); i++) {
int y = edge[x][i].to;
if (dis[y] > dis[x] + edge[x][i].to) {
dis[y] = dis[x] + edge[x][i].to;
if (!vis[y]) {
vis[y] = true;
if (!Q.empty() && dis[y] < dis[Q.front()])//加入队首
Q.push_front(y);
else//加入队尾
Q.push_back(y);
}
}
}
}
}
标准
struct Edge {
int to, next;
int dis;
} edge[N];
int head[N], tot;
bool vis[N];
int dis[N];
void addEdge(int x, int y, int dis) {
edge[++tot].to = y;
edge[tot].dis = dis;
edge[tot].next = head[x];
head[x] = tot;
}
void SPFA(int S) {
memset(vis, false, sizeof(vis));
memset(dis, INF, sizeof(dis));
dis[S] = 0;
queue<int> Q;
Q.push(S);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = false;
for (int i = head[x]; i != -1; i = edge[i].next) {
int to = edge[i].to;
if (dis[to] > dis[x] + edge[i].dis) {
dis[to] = dis[x] + edge[i].dis;
if (!vis[to]) {
vis[to] = true;
Q.push(to);
}
}
}
}
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++){
int x,y,dis;
scanf("%d%d%d",&x,&y,&dis);
//无向图添边两次
addEdge(x,y,dis);
addEdge(y,x,dis);
}
int S;
scanf("%d",&S);
SPFA(S);
for(int i=1;i<=n;i++)
printf("%d\n",dis[i]);
}
return 0;
}
用spfa判断负环
struct Edge {
int from, to;
int dis;
Edge() {}
Edge(int from, int to, int dis) : from(from), to(to), dis(dis) {}
};
struct SPFA {
int n, m;
Edge edges[N]; //所有的边信息
int head[N]; //每个节点邻接表的头
int next[N]; //每个点的下一条边
int pre[N]; //最短路中的上一条弧
bool vis[N];
int dis[N];
int cnt[N]; //进队次数
void init(int n) {
this->n = n;
this->m = 0;
memset(head, -1, sizeof(head));
}
void AddEdge(int from, int to, int dist) {
edges[m] = Edge(from, to, dist);
next[m] = head[from];
head[from] = m++;
}
bool negativeCycle(int s) { //判负环
memset(vis, false, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
memset(dis, INF, sizeof(dis));
dis[s] = 0;
queue<int> Q;
Q.push(s);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = false;
for (int i = head[x]; i != -1; i = next[i]) {
Edge &e = edges[i];
if (dis[e.to] > dis[x] + e.dis) {
dis[e.to] = dis[x] + e.dis;
pre[e.to] = i;
if (!vis[e.to]) {
vis[e.to] = true;
Q.push(e.to);
if (++cnt[e.to] > n)
return true;
}
}
}
}
return false;
}
} spfa;
int main() {
int n, m;
while (scanf("%d%d", &n, &m) != EOF) {
spfa.init(n);
int S;
scanf("%d", &S);
for (int i = 1; i <= m; i++) {
int x, y, dis;
scanf("%d%d%d", &x, &y, &dis);
//无向边添边两次
spfa.AddEdge(x, y, dis);
spfa.AddEdge(y, x, dis);
}
spfa.negativeCycle(S);
for (int i = 1; i <= n; i++)
printf("%d\n", spfa.dis[i]);
}
return 0;
}
差分约束
求解差分约束系统的关键在于建图,建好图后使用 SPFA 算法直接判断有无负环即可判断该差分约束系统有无解。
具体方法:
1.新建一个图,n 个变量看作 n 个点,m 个约束条件作为 m 条边,每个顶点 Vi 分别对于一个未知量,每个有向边对应两个未知量的不等式。
1)对于 <= 的不等式,形如:dis[j]-dis[i]<=w,可化为 dis[j]<=dis[i]+w,建立从 i 到 j 权值为 w 的边
2)对于 >= 的不等式,形如:dis[j]-dis[i]>=w,可化为 dis[i]<=dis[j]-w,建立从 j 到 i 权值为 -w 的边
2.根据约束条件,进行初始化
1)若求差最大,则:dis[1]=0 且 dis[i]=INF
2)若求差最小,则:dis[1]=0 且 dis[i]=-INF
3.根据实际情况判断是否需要添加超级源点 Vs(0 号点)
1)若建成的图能保证连通,则直接求最短路即可,若建成的图不是连通图,为保证图的连通性,需要加一个源点 Vs,从 Vs 到任意点 Vi 的边权为 0,即:W(Vs,Vi)=0,然后从该点出发进行计算,那么最终求出源点 Vs 到其他所有点的最短距离就是差分约束系统的一个可行解,且可行解之间的差距最小。
原因:添加从虚点 Vs 到各个顶点 Vi 的权为 0 的边,是为了保证构造出的图是连通的,且由于虚点本身并不引入负环,因此设置虚点 Vs 后最短路仍然存在且每个约束仍满足。
2)若不添加超级源点,只是将初始距离设为 INF,且令其中一个点的初始距离为 0,然后就该点到其他所有点的最短距离,那么最短距离的集合就是一个可行解,且该可行解两两之间差距最大。
适用情况:保证问题一定存在解,即:不存在负环(否则从 1 号点到其他点没有路,但其他点的强连通分量中有负环)
4.根据结果进行解的判断
1)若源点 Vs 到某点 Xi 不存在最短路径,即:dis[Xi]=INF,则表示该点 Xi 表示的变量可取任意解,均能满足差分约束的要求
2)若存在源点 Vs 到某点 Xi 的最短路径,则得到该点 Xi 表示的变量的最大值
3)若求最短路的过程中出现负环,则该差分约束系统无解。
注:若所有的未知量都是正数,则不会存在负环,使用 Dijkstra 求解单源最短路即可;若存在负数,需使用 SPFA 来求解,判断有无负环
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define N 1000001
struct Edge{
int from;
int to;
int w;
Edge(){}
Edge(int from,int to,int w):from(from),to(to),w(w){}
}edge[N];
int head[N],next[N],num;
int dis[N];
bool vis[N];
void add(int from,int to,int w){
edge[num]=Edge(from,to,w);
next[num]=head[from];
head[from]=num++;
}
struct HeapNode{
int dis;
int x;
HeapNode(int dis,int x):dis(dis),x(x){}
bool operator < (const HeapNode &rhs) const{
return dis>rhs.dis;
}
};
int dijkstra(int n){
for(int i=0;i<n;i++)
dis[i]=INF;
dis[0]=0;
priority_queue<HeapNode> Q;
Q.push(HeapNode(dis[0],0));
while(!Q.empty()){
HeapNode x=Q.top();
Q.pop();
int u=x.x;
if(vis[u])
continue;
vis[u]=true;
for(int i=head[u];i!=-1;i=next[i]){
Edge &e=edge[i];
if(dis[e.to]>dis[u]+e.w){
dis[e.to]=dis[u]+e.w;
Q.push(HeapNode(dis[e.to],e.to));
}
}
}
return dis[n-1];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
num=0;
memset(head,-1,sizeof(head));
while(m--){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
x--,y--;
add(x,y,w);
}
int res=dijkstra(n);
if(res==INF)
printf("arbitrary");
else//源点到终点的最大值
printf("%d\n",res);
return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define N 10001
struct Edge{
int from;
int to;
int w;
Edge(){}
Edge(int from,int to,int w):from(from),to(to),w(w){}
}edge[N];
int head[N],next[N],num;
int dis[N];
int outque[N];//记录出队次数
bool vis[N];
void init(){
num=0;
memset(head,-1,sizeof(head));
}
void add(int from,int to,int w){
edge[num]=Edge(from,to,w);
next[num]=head[from];
head[from]=num++;
}
void SPFA(int s,int n){
int res=0;
memset(vis,false,sizeof(vis));
memset(outque,0,sizeof(outque));
for(int i=1;i<n;i++)
dis[i]=INF;
dis[s]=0;
vis[s]=true;
queue<int> Q;
Q.push(s);
while(!Q.empty()){
int x=Q.front();
Q.pop();
vis[x]=false;
outque[x]++;
if(outque[x]>n-1){//如果出队次数大于n,说明存在负环
res=-1;
break;
}
for(int i=head[x];i!=-1;i=next[i]){
Edge &e=edge[i];
if(dis[e.to]>dis[x]+e.w){
dis[e.to]=dis[x]+e.w;
if(!vis[e.to]){
vis[e.to]=true;
Q.push(e.to);
}
}
}
}
if(res==-1)//出现负环,不存在可行解
printf("-1\n");
else if(dis[n]==INF)//可取任意值
printf("arbitrary\n");
else//源点s到终点的最大值
printf("%d\n",dis[n]);
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
init();
for(int i=0;i<m;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);//建边x-y<=w
}
//虚拟节点0号到各点的边
//for(int i=1;i<=n;i++)
//add(0,i,0);
SPFA(1,n);//求源点s到终点的最大值,若添加虚拟节点0号后,需改为SPFA(0,n+1)
}
return 0;
}
P5318
你或许需要 一个结构体存储父亲和儿子
需要一个动态数组来存放这些结构体
需要一个动态数组存具体二维信息
输入关系,然后打包成结构体存进一个数组里,创造一个cmp,排序大头和小头
好,排序好的是从第一本到最后一本,下面是第一本下面第一个要看的到最后一个要看到的,每一个都有它对应的结构体里的爸爸,所以再次遍历,一个数组里存结构体里的爸爸,爸爸下面插入,插入本身在数组里就是有顺序的,然后插入的就是i 就是向向自己的爸爸中插入自己 由此形成了可以深搜广搜的结构
用一个结构体vector(为了节省空间,咱用vector来存)存储每个边的起点和终点,然后用一个二维vector(也就是一个vector数组)存储边的信息。
这个存储方法可能有点难理解,不过其实也没那么难:我们用ee[aa][bb]=cc,来表示顶点aa的第bb条边是cc号边。咱举个栗子,还是拿样例说吧:
8 9
1 2 //0号边(由于vector的下标是从0开始的,咱就“入乡随俗”,从0开始)
1 3 //1号边
1 4 //2号边
2 5 //3号边
2 6 //4号边
3 7 //5号边
4 7 //6号边
4 8 //7号边
7 8 //8号边
0 1 2 //1号顶点连着0、1、2号边
3 4 //2号顶点连着3、4号边
5 //3号顶点连着5号边
6 7 //4号顶点连着6、7号边
//5号顶点没有边
//6号顶点没有边
8 //7号顶点连着8号边
//8号顶点没有边
广搜用队列,把1入队,
队伍中,找出对头,循环里面是对对头的儿子们遍历,每一次找出他们的儿子,然后看儿子是否入队,没有就入对并且打印;最后记得出对一个
void bfs(int x){ //广度优先遍历
queue <int> q;
q.push(x);
cout<<x<<" ";
vis2[x]=1;
while(!q.empty()){
int fro=q.front();
for(int i=0;i<e[fro].size();i++){
int point=s[e[fro][i]].v;
if(!vis2[point]){
q.push(point);
cout<<point<<" ";
vis2[point]=1;
}
}
q.pop();
}
}
深度优先搜索
用递归,先进来,然后找儿子,找儿子,步步深入
void dfs(int x){ //深度优先遍历
vis1[x]=1;
cout<<x<<" ";
for(int i=0;i<e[x].size();i++){
int point=s[e[x][i]].v;
if(!vis1[point]){
dfs(point);
}
}
}
/*#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int vis1[100001],vis2[1000001];
struct edge
{
int u;
int v;
};/存储每一对关系
int m,n,x,y;
bool cmp(edge x,edge y)
{
if(x.v==y.v ) return x.u <y.u ;
else return x.v <y.u ;}
vector<edge>s;//存储关系的数组
vector<int>e[1000000];//存储每一文章里的文献是第几个
void dfs(int x)///深搜:由1个点,找到他的那些小的
{
vis1[x]=1;
cout<<x<<" ";
for(int i=0;i<e[x].size();i++)
{
int point=s[e[x][i]].v;
if(!vis1[point])
{
dfs(point);
}
}
}
void bfs(int x)
{
queue<int> q;
q.push(x);
cout<<x<<" ";
vis2[x]=1;
while(!q.empty())
{
int fro=q.front();
for(int i=0;i<e[fro].size();i++)
{
int point=s[e[fro][i]].v;
if(!vis2[point])
{
q.push(point);
cout<<point<<" ";
vis2[point]=1;
}
}
q.pop();
}
}
int main()
{
cin>>m>>n;
for(int i=0;i<n;i++)
{
cin>>x>>y;
s.push_back((edge){x,y});
}
sort(s.begin(),s.end(),cmp);
for(int i=0;i<n;i++)///一定要从0开始,而不是1
{
e[s[i].u].push_back(i);
}
dfs(1);
cout<<endl;
bfs(1);
return 0;
}*/
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
struct edge{
int u,v;
};
vector <int> e[100001];
vector <edge> s;
bool vis1[100001]={0},vis2[100001]={0};
bool cmp(edge x,edge y){
if(x.v==y.v)
return x.u<y.u;
else return x.v<y.v;
}
void dfs(int x){
vis1[x]=1;
cout<<x<<" ";
for(int i=0;i<e[x].size();i++){
if(!vis1[e[x][i]){
dfs(e[x][i]);
}
}
}
void bfs(int x){
queue <int> q;
q.push(x);
cout<<x<<" ";
vis2[x]=1;
while(!q.empty()){
int fro=q.front();
for(int i=0;i<e[fro].size();i++){
int point=s[e[fro][i]].v;
if(!vis2[point]){
q.push(point);
cout<<point<<" ";
vis2[point]=1;
}
}
q.pop();
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int uu,vv;
cin>>uu>>vv;
s.push_back((edge){uu,vv});
}
sort(s.begin(),s.end(),cmp);
for(int i=0;i<m;i++)
e[s[i].u].push_back(s[i].v);
dfs(1);
cout<<endl;
bfs(1);
}
#include<bits/stdc++.h>
using namespace std;
int n,m;
set<int>e[100009];
bool vis[100009];
void init(){
for(scanf("%d%d",&n,&m);m;--m){
int u=0,v=0;
scanf("%d%d",&u,&v);
e[u].insert(v);
}
}
void dfs(int x=1){
if(vis[x])return;vis[x]=1;
printf("%d ",x);
for(int v:e[x])dfs(v);
}
void bfs(){
queue<int>q;
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
if(vis[x])continue;vis[x]=1;
printf("%d ",x);
for(int v:e[x])q.push(v);
}
}
int main(){
init();
dfs();
putchar('\n');
memset(vis,0,sizeof(vis));
bfs();
putchar('\n');
return 0;
}
P3916
出发到达的最远的点:反向考虑最远的点在哪,循环从n到1,则每个点i能访问到的结点的a值都是i,每个点访问一次,这个a最优
反向存边+从大到小便利+有过就返回==确保每个都是最远的,dfs里不变的参量也是有价值的,可以用来赋值
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
#define MAXL 100010
int N, M, A[MAXL];
vector<int> G[MAXL]; //vector存图
void dfs(int x, int d) {
if(A[x]) return; //访问过
A[x] = d;这个也太diao了吧
for(int i=0; i<G[x].size(); i++)
dfs(G[x][i], d);
}
int main() {
int u, v;
scanf("%d%d", &N, &M);
for(int i=1; i<=M; i++) {
scanf("%d%d", &u, &v);
G[v].push_back(u); //反向建边
}
for(int i=N; i; i--) dfs(i, i);
for(int i=1; i<=N; i++) printf("%d ", A[i]);
printf("\n");
return 0;
}
P1113
其实就是寻找一条最长的链,应该先把自己的时间和工作匹配对应上,然后取自己的子任务中较长的为准,以此类推向后
拓扑排序模板题目,每一次记得更新
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int>ru[9000];
queue<int>qu;
int m,h,g,rut[9000],dp[9000],tim[9000] ,p,ans;
int main()
{
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>h>>g;
tim[h]=g;
while(cin>>p&&p)///很多个就是while
{
ru[h].push_back(p);//存到我的代做列表里
rut[p]++;//为了后面寻找开头的事项
}
}
for(int i=1;i<=m;i++)
{
if(!rut[i])//有点拓扑的味道
{
qu.push(i);
dp[i]=tim[i];
}
}
while(!qu.empty())
{
int now=qu.front();//就是干完的,他的下面的任务可以开始了
qu.pop();
for(int i=0;i<ru[now].size();i++)//下面的任务
{
int next=ru[now][i];//它的子任务
dp[next]=max(dp[next],dp[now]+tim[next]);//而完成工作 X 所需的时间和所有 X 所依赖的工作完成的时间的最大值有关。(就是说应该每一次都更新,因为你很可能是从多个任务过来的,所以每一次新的入队对于这些还没有入对的要有新的更新)
rut[next]--;//每个子任务的入度-1,就是拓扑
if(rut[next]==0)
qu.push(next);//这是一个累加的过程
}
}
for(int i=1;i<=m;i++)
{
ans=max(ans,dp[i]);就是最后一个完成任务的时间啊
}
cout<<ans;
return 0;
}
P1807
带权有向无环图
或许floyed可以
#include<iostream>
using namespace std;
const int inf=0x7fffffff;
int n,m,x,y,z,map[1007][1007];
int main()
{
cin>>n>>m;
if(m==0) {
cout<<-1;
return 0;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i==j) map[i][j]=0;
else map[i][j]=inf;
}///初始化
}
for(int i=0;i<m;i++)
{
cin>>x>>y>>z;
x--;y--;
map[x][y]=min(map[x][y],-z); 更新
}
for(int k=0;k<n;k++)//dp
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(map[i][k]!=inf&&map[k][j]!=inf)
map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
}
}
}
if(map[0][n-1]==inf) cout<<-1;
else cout<<-map[0][n-1];
}
当然,广搜也是可以的
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=1502;
queue<int>q;//广搜队列
int d[maxn],mp[maxn][maxn];//d[i]记录i结点前的最长路,mp[a][b]存储结点a,b间的路径长
int n,m;
void bfs()
{
memset(d,-1,sizeof(d)); //初始化为-1,便于无解时输出的d[n]为-1
d[1]=0;//但第一个结点前路径长为0
q.push(1);
while(!q.empty())
{
int t=q.front();
q.pop();
for(int i=1;i<=n;i++)
if(mp[t][i]&&d[i]<d[t]+mp[t][i])//如果从队列头到当前结点i有边,但不是最长(即此点的最长路小于队列头最长路加t-i的路径长)
{
d[i]=d[t]+mp[t][i];//更新最长路
q.push(i);//进入队列,搜索下一节点
}
}
}
int main()
{
cin>>n>>m;
int a,b,v;
for(int i=1;i<=m;i++)
{
cin>>a>>b>>v;
mp[a][b]=max(mp[a][b],v);//如果两点之间有多条连边,只需保留最长边
}
bfs();
cout<<d[n];//输出最后一点的最长路即可
return 0;
}