P1262_美帝的间谍网络被我部捕获!

题面

这道题太神了吧,从昨晚七点半做到今天下午两点.

我经历了以下折腾(以下内容可跳过):

  • 一开始想的是用Tarjan缩点,然后跑最短路,通过路径染色,让一条路径上的点的权值等于起点(也就是可以被收买的美国间谍的编号),然后枚举每一个点,用它对应的路径,累加权值得到答案,如果有点的权值为正无穷,那么就说明有间谍无法收买,再扫一遍染色数组找出编号最小的点输出NO.

这种方式貌似可以,但是在跑最短路的时候,松弛操作会把权值更小的路径更新进来,但是这条最短路可能是无法确保跑向权值更大的点的前缀点的,所以说会导致权值反而更大.这种方法最高得了52分.

比如下面这张图:

P1262_美帝的间谍网络被我部捕获!配图1.png

1,2,3,4号点明显是个强联通分量,收买这个分量的代价为10.

5号点也是一个强连通分量,收买它的代价为20.

该图只有从5号点开始,才可以遍历完所有的点,总代价为20.

但是在跑最短路时,进入缩过点后的1,2,3,4节点后,会把这个点的权值松弛为10,但是这个10是无法跑完整个图的,这也导致总代价变为了30;

  • 然后我甚至想缩点之后跑一边最小生成树,但是算了下这样做会超时.

  • 之后我又想好了好久,和zxs大佬在中午恰饭的时候交流了下这道题,才想出来可以记录下所有入度为0的点,然后累加这些点的权值.这是因为入度为0的点是一定要被收买的,不然就无法遍历完全图,如果有间谍点无法被收买而且无法被其他间谍告发,就输出NO.

还有一些细节在代码里解释罢,各位在阅读我的变量名时只用看下划线后的部分.

代码:


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>

using namespace std;

stack<int>s;

struct edge
{
    int to,next;
}e[10010];

int OddToThePeoplesVolunteerArmy_n,GreatSoviet_p,TheSovietUnion_CntSize,PeoplesRupublic_TimeCnt,YpaForOurGreatMotherland_r,TheDefenderOfMoscow_size,TheInternationalMustCometrue_ans;
int head[10010],dfn[10010],low[10010],indu[10010],dis[10010],color[10010],Nodedis[10010];
bool flag[10010];

void EdgeAdd(int,int);
void Tarjan(int);

int main()
{
    memset(head,-1,sizeof(head));
    memset(Nodedis,0x3f,sizeof(Nodedis));
    memset(dis,0x3f,sizeof(dis));
    scanf("%d%d",&OddToThePeoplesVolunteerArmy_n,&GreatSoviet_p);
    for(int _=1;_<=GreatSoviet_p;_++)
    {
        int id,USAIsRubbish;
        scanf("%d%d",&id,&USAIsRubbish);
        dis[id]=USAIsRubbish;
    }
    scanf("%d",&YpaForOurGreatMotherland_r);
    for(int _=1;_<=YpaForOurGreatMotherland_r;_++)
    {
        int father,son;
        scanf("%d%d",&father,&son);
        EdgeAdd(father,son);
    }
    for(int _=1;_<=OddToThePeoplesVolunteerArmy_n;_++)
    {
        if(dfn[_]==0&&dis[_]!=0x3f3f3f3f)//把可以收买的美国间谍进入Tarjan缩点,这样缩出来的点才能够被遍历.
        {
            Tarjan(_);
        }
    }
    for(int _=1;_<=OddToThePeoplesVolunteerArmy_n;_++)//缩完点后如果存在无法被收买,又无法被其它间谍指控的点,就说明无法收买所有间谍.
    {
        if(dfn[_]==0)
        {
            printf("NO\n%d\n",_);
            return 0;
        }
    }
    for(int _=1;_<=OddToThePeoplesVolunteerArmy_n;_++)
    {
        for(int __=head[_];__!=-1;__=e[__].next)
        {
            int to=e[__].to;
            if(color[_]!=color[to])
            {
                indu[color[to]]++;//统计入度.
            }
        }
    }
    for(int _=1;_<=TheSovietUnion_CntSize;_++)
    {
        if(indu[_]==0)//累加必须收买的间谍的代价.
        {
            TheInternationalMustCometrue_ans+=Nodedis[_];
//            printf("Nodedis:%d\n",Nodedis[_]);
        }
    }
    printf("YES\n%d\n",TheInternationalMustCometrue_ans);
return 0;
}

void EdgeAdd(int from,int to)
{
    e[++TheDefenderOfMoscow_size].to=to;
    e[TheDefenderOfMoscow_size].next=head[from];
    head[from]=TheDefenderOfMoscow_size;
}

void Tarjan(int FuckTrump_from)
{
    dfn[FuckTrump_from]=low[FuckTrump_from]=++PeoplesRupublic_TimeCnt;
    s.push(FuckTrump_from);
    flag[FuckTrump_from]=true;
    for(int _=head[FuckTrump_from];_!=-1;_=e[_].next)
    {
        int to=e[_].to;
        if(dfn[to]==0)
        {
            Tarjan(to);
            low[FuckTrump_from]=min(low[FuckTrump_from],low[to]);
        }
        else if(flag[to]==true)
        {
            low[FuckTrump_from]=min(low[FuckTrump_from],dfn[to]);
        }
    }
    if(dfn[FuckTrump_from]==low[FuckTrump_from])
    {
        TheSovietUnion_CntSize++;
        while(!s.empty())
        {
            int RedAmryIsTheStrongest_temp=s.top();
            s.pop();
            flag[RedAmryIsTheStrongest_temp]=false;
            color[RedAmryIsTheStrongest_temp]=TheSovietUnion_CntSize;
            Nodedis[TheSovietUnion_CntSize]=min(Nodedis[TheSovietUnion_CntSize],dis[RedAmryIsTheStrongest_temp]);//缩点后的点的代价为原来的环的代价中最小的那个代价.
            color[RedAmryIsTheStrongest_temp]=TheSovietUnion_CntSize;//染色.
            if(RedAmryIsTheStrongest_temp==FuckTrump_from)break;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/Lemir3/p/11097721.html