目录
思路:
这道题让我们求最小的"k"的值,其实就是让我们求权值最大的点的权值。而求一个图中点的权值,不难让我们联想到拓扑排序算法。至此,思路已经清晰。
思维过程:
一、图论第一步——存图
有图论必存图,首要的便是存图。这道题数据不大,怎么存都可以。我这里推荐用链式前向星的方法存储,这样节省空间。代码如下:
const int N = 1e3 + 2;
const int M = 1e4 + 2;
int edge[M]; //第i条边的"终点"是edge[i]
int head[N]; //第i个点引出的最后一条边的编号是head[i]
int last[M]; //第i条边的"起点"引出的上一条边的编号是last[i]
int tot = 0; //总边数
void add(int x, int y) //建边<x, y>
{
edge[++tot] = y;
last[tot] = head[x];
head[x] = tot;
}
二、输入问题——不存负边权
我认为这个问题是很重要的,因为我因为这个问题挂了(60分惨剧)。注意,给边权赋值时要特判,不能有负边权,不然会挂掉,还找不出原因。
剩下的大家都很熟悉了,就在代码里看吧!
题解:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 2;
const int M = 1e4 + 2;
int edge[M];
int head[N];
int last[M];
int ins[N]; //存入度
int valn[N]; //存点权
int vale[M]; //存边权
int n, m, tot = 0;
void add(int x, int y) //建边
{
edge[++tot] = y;
last[tot] = head[x];
head[x] = tot;
}
bool topsort() //拓扑排序
{
int x = 0;
queue<int> q;
for(int i = 1; i <= n; i++)
{
if(!ins[i]) //ins[i] == 0, 入度为0
q.push(i);
}
while(!q.empty()) //不空
{
int u = q.front();
q.pop();
x++;
for(int i = head[u]; i; i = last[i])
{
int v = edge[i];
valn[v] = max(valn[v], valn[u] + vale[i]); //更新点权
if(vale[i] == 0) //同步点权
valn[u] = valn[v];
ins[v]--; //减入度
if(!ins[v])
q.push(v);
}
}
return x == n;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++)
{
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
if(z > 0) //特判边权
{
ins[x]++;
add(y, x);
vale[tot] = z;
}
else
{
add(x, y);
ins[y]++;
vale[tot] = -z; //注意,小细节
}
}
if(topsort()) //求最大的点权
{
int maxn = -0xfffffff;
for(int i = 1; i <= n; i++)
maxn = max(maxn, valn[i]);
printf("%d", maxn);
}
else
printf("NO");
return 0;
}