Description
给定一个带权有向图,边权可能为负,输出边数最小的正环,并保证边数最小的情况下使环内边权和最大,输出最大边权和。 2<=N<=300,2<=M<=N*(N-1)
Analysis
并不知道SPFA能不能做,好像是水法 弄一个邻接矩阵
Ak[N][N]
,我们可以像矩阵乘法一样乘法若干次,求出某点走若干步到某点的最长路,那么
Ak
就表示乘了k次 于是,朴素想法可以乘一次判断一次,但是乘法为O(n^3)的,总复杂度为O(n^4) TLE 一次一次乘太慢了,我们可以采用倍增的思想
Ak
改成表示乘
2k
后的结果 一直乘,直到满足条件的前一个k 然后,可以以A的2的幂相乘得到最后的答案,k从大到小,倍增的思想只要不合法就乘 时间复杂度优化为
O(n3logn)
注意,本题需要一定卡常技巧
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=
305;
typedef ll mat[N][N];
int _2[N],n,m;
mat a,b,c[N];
ll pd(mat a)
{
ll mx=
0;
fo(i,
1,n) mx=max(mx,a[i][i]);
return mx;
}
void mul(mat &c,mat a)
{
memset(c,
0,
sizeof(c));
}
int main()
{
freopen(
"utrka.in",
"r",stdin);
freopen(
"utrka.out",
"w",stdout);
_2[
0]=
1;
fo(i,
1,
20) _2[i]=_2[i-
1]*
2;
int u,v,x,y;
scanf(
"%d %d",&n,&m);
memset(c,
200,
sizeof(c));
fo(i,
1,m)
{
scanf(
"%d %d %d %d",&u,&v,&x,&y);
c[
0][u][v]=y-x;
}
int l=
1;
for(;;l++)
{
memcpy(c[l],c[l-
1],
sizeof(c[l-
1]));
fo(i,
1,n)
fo(j,
1,n)
fo(k,
1,n)
c[l][i][j]=max(c[l][i][j],c[l-
1][i][k]+c[l-
1][k][j]);
if(pd(c[l]))
break;
}
l--;
memcpy(a,c[l],
sizeof(c[l]));
int ans=_2[l];
while(--l>=
0)
{
memcpy(b,a,
sizeof(a));
fo(i,
1,n)
fo(j,
1,n)
fo(k,
1,n)
b[i][j]=max(b[i][j],a[i][k]+c[l][k][j]);
if(!pd(b))
{
memcpy(a,b,
sizeof(b));
ans+=_2[l];
}
}
printf(
"%d ",ans+
1);
memcpy(b,a,
sizeof(a));
fo(i,
1,n)
fo(j,
1,n)
fo(k,
1,n)
b[i][j]=max(b[i][j],a[i][k]+c[
0][k][j]);
printf(
"%d",pd(b));
return 0;
}
转载请注明原文地址: https://ju.6miu.com/read-11165.html