Grass Cownoisseur[Usaco2015 Jan]

本文详细解析了一道USACO竞赛题目——牛吃草问题,通过使用Tarjan算法进行强连通分量缩点,并结合SPFA算法求解最短路径问题,最终实现了在有向图中寻找特定路径的目标。

题目描述

In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1..N, with each one-way cow path connecting a pair of fields. For example, if a path connects from field X to field Y, then cows are allowed to travel from X to Y but not from Y to X. Bessie the cow, as we all know, enjoys eating grass from as many fields as possible. She always starts in field 1 at the beginning of the day and visits a sequence of fields, returning to field 1 at the end of the day. She tries to maximize the number of distinct fields along her route, since she gets to eat the grass in each one (if she visits a field multiple times, she only eats the grass there once). As one might imagine, Bessie is not particularly happy about the one-way restriction on FJ's paths, since this will likely reduce the number of distinct fields she can possibly visit along her daily route. She wonders how much grass she will be able to eat if she breaks the rules and follows up to one path in the wrong direction. Please compute the maximum number of distinct fields she can visit along a route starting and ending at field 1, where she can follow up to one path along the route in the wrong direction. Bessie can only travel backwards at most once in her journey. In particular, she cannot even take the same path backwards twice. 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)

输入

The first line of input contains N and M, giving the number of fields and the number of one-way paths (1 <= N, M <= 100,000). The following M lines each describe a one-way cow path. Each line contains two distinct field numbers X and Y, corresponding to a cow path from X to Y. The same cow path will never appear more than once.

输出

A single line indicating the maximum number of distinct fields Bessie
can visit along a route starting and ending at field 1, given that she can
follow at most one path along this route in the wrong direction.
 

样例输入

7 10
1 2
3 1
2 5
2 4
3 7
3 5
3 6
6 5
7 2
4 7

样例输出

6

题解
   考试的时候调这道题的深搜调了很久,真是没法拯救的运行错误,浪费了很多时间最后还是全部注释掉。凭经验Usaco的题输出样例骗了10分。。。隐约感觉应该用tarjan,虽然前几天才看过但是并没有把握打准确,何况就算缩完点也不知道应该怎么处理。曾几何时做着tarjan的题复习spfa,如今做着综合题复习tarjan和spfa,时间过得多么快啊。
    正解思路清晰而实现复杂。输入建原图,用tarjan缩点建出新图和边全部反向的图,点权即为强连通分量中的点数。然后在两个新图中各自从1所属的点跑一遍spfa,得出1到所有点和所有点到1的距离。把强连通分量里的边反向是没有意义的,所以枚举DAG(新图)中的所有边,它联通了一个回路,回路长为起点到1和1到终点的最长路之和。最后要注意的地方,算最长路时把1所在强连通分量计算在内,所以结果中应该除去重复加上的w[c[1]];第二点是如果正反任何一个最长路为0代表它不连通,这样的边是不能记为结果的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int sj=100010;
int dfn[sj],low[sj],c[sj],jl[sj],dis[sj],s[sj];
int o[sj],h[sj],l[sj],w[sj];
int m,n,e1,e2,e3,cnt,ge,zh,ans;
bool r[sj];
struct B
{
    int v,ne;
}b[sj];
struct BS 
{
     int u,to,nex;
}bs[sj];
struct BSS 
{
     int to,nex;
}bss[sj];
inline int re()
{
    int jg=0,jk=0;
    jk=getchar()-'0';
    if(jk>=0&&jk<=9)  jg+=jk;
    jk=getchar()-'0';
    while(jk>=0&&jk<=9)
    {
       jg*=10;
       jg+=jk;
       jk=getchar()-'0';
    }
    return jg;
}
void add(int x,int y)
{
     b[e1].v=y;
     b[e1].ne=h[x];
     h[x]=e1++;
}
void ad2(int x,int y)
{
     bs[e2].to=y;
     bs[e2].nex=l[x];
     bs[e2].u=x;
     l[x]=e2++;
}
void ad3(int x,int y)
{
     bss[e3].to=y;
     bss[e3].nex=o[x];
     o[x]=e3++;
}
void init()
{
     n=re();
     m=re();
     memset(h,-1,sizeof(h));
     memset(l,-1,sizeof(l));
     memset(o,-1,sizeof(o));
     int a1,a2;
     for(int i=1;i<=m;i++)
     {
        a1=re();
        a2=re();
        add(a1,a2);
     }
}
int bj(int x,int y)
{
     return x<y?x:y;
}
void spfa1(int x)
{
     dis[x]=w[x];
     r[x]=1;
     queue<int> q;
     q.push(x);
     int f;
     while(!q.empty())
     {
       f=q.front();
       for(int i=l[f];i!=-1;i=bs[i].nex)
       {
         if(dis[bs[i].to]<dis[f]+w[bs[i].to])
         {
           dis[bs[i].to]=dis[f]+w[bs[i].to];
           if(r[bs[i].to]!=1)
           {
              q.push(bs[i].to);
              r[bs[i].to]=1;
           }
         }
         }
       r[f]=0;
       q.pop();
     }
}
void spfa2(int x)
{
     dis[x]=w[x];
     r[x]=1;
     queue<int> q;
     q.push(x);
     int f;
     while(!q.empty())
     {
       f=q.front();
       for(int i=o[f];i!=-1;i=bss[i].nex)
         if(dis[bss[i].to]<dis[f]+w[bss[i].to])
         {
           dis[bss[i].to]=dis[f]+w[bss[i].to];
           if(r[bss[i].to]!=1)
           {
              q.push(bss[i].to);
              r[bss[i].to]=1;
           }
         }
       r[f]=0;
       q.pop();
     }
}
void tarjan(int x)
{
     dfn[x]=low[x]=++cnt;
     s[++ge]=x;
     r[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
     {
        if(!dfn[b[i].v])
        {
           tarjan(b[i].v);
           low[x]=bj(low[x],low[b[i].v]);
        }
        else if(r[b[i].v])
           low[x]=bj(low[x],dfn[b[i].v]);
     }
     if(low[x]==dfn[x])
     {
        int li;
        zh++;
        do
        {
             li=s[ge--];
             c[li]=zh;
             w[zh]++;
             r[li]=0;
        }while(li!=x);
     }
}
int db(int &x,int y)
{
    x=x>y?x:y;
}
int main()
{
    init();
    for(int i=1;i<=n;i++)
      if(!dfn[i])
        tarjan(i);
    for(int i=1;i<=n;i++)
      for(int j=h[i];j!=-1;j=b[j].ne)
        if(c[i]!=c[b[j].v])
        {
          ad2(c[i],c[b[j].v]);
          ad3(c[b[j].v],c[i]);
        }
    memset(r,0,sizeof(r));
    memset(dis,0,sizeof(dis));
    spfa1(c[1]);
    for(int i=1;i<=zh;i++)
      jl[i]=dis[i];
    memset(r,0,sizeof(r));
    memset(dis,0,sizeof(dis));
    spfa2(c[1]);
    ans=1;
    for(int i=0;i<e2;i++)
      if(jl[bs[i].to]&&dis[bs[i].u])
       db(ans,jl[bs[i].to]+dis[bs[i].u]-w[c[1]]);
    printf("%d",ans);
    return 0;
}

 

 
 

转载于:https://www.cnblogs.com/moyiii-/p/7182957.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值