[BZOJ1924/Luogu2403][Sdoi2010]所驼门王的宝藏(Tarjan,动态规划)

发布于 2018-02-20  87 次阅读


本文章由SYCstudio或本站其它作者所有,请勿擅自转载

本文链接地址:[BZOJ1924/Luogu2403][Sdoi2010]所驼门王的宝藏(Tarjan,动态规划)

Description

在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族。被族人恭称为“先知”的Alpaca L. Sotomon是这个家族的领袖,外人也称其为“所驼门王”。所驼门王毕生致力于维护家族的安定与和谐,他曾亲自率军粉碎河蟹帝国主义的野蛮侵略,为族人立下赫赫战功。所驼门王一生财宝无数,但因其生性节俭低调,他将财宝埋藏在自己设计的地下宫殿里,这也是今天Henry Curtis故事的起点。Henry是一个爱财如命的贪婪家伙,而又非常聪明,他费尽心机谋划了这次盗窃行动,破解重重机关后来到这座地下宫殿前。
整座宫殿呈矩阵状,由R×C间矩形宫室组成,其中有N间宫室里埋藏着宝藏,称作藏宝宫室。宫殿里外、相邻宫室间都由坚硬的实体墙阻隔,由一间宫室到达另一间只能通过所驼门王独创的移动方式——传送门。所驼门王为这N间藏宝宫室每间都架设了一扇传送门,没有宝藏的宫室不设传送门,所有的宫室传送门分为三种:
“横天门”:由该门可以传送到同行的任一宫室;
“纵寰门”:由该门可以传送到同列的任一宫室;
“自由门”:由该门可以传送到以该门所在宫室为中心周围8格中任一宫室(如果目标宫室存在的话)。
深谋远虑的Henry当然事先就搞到了所驼门王当年的宫殿招标册,书册上详细记录了每扇传送门所属宫室及类型。而且,虽然宫殿内外相隔,但他自行准备了一种便携式传送门,可将自己传送到殿内任意一间宫室开始寻宝,并在任意一间宫室结束后传送出宫。整座宫殿只许进出一次,且便携门无法进行宫室之间的传送。不过好在宫室内传送门的使用没有次数限制,每间宫室也可以多次出入。
现在Henry已经打开了便携门,即将选择一间宫室进入。为得到尽多宝藏,他希望安排一条路线,使走过的不同藏宝宫室尽可能多。请你告诉Henry这条路线最多行经不同藏宝宫室的数目。

Http

BZOJ
Luogu

Tag

Tarjan,动态规划

题目大意

在有向图上求一条可以经过重复点的经过点最多的路径。

解决思路

思路比较简单,首先\(Tarjan\)缩点后记录每一个缩的点的位置,然后按\(Top\)序在\(DAG\)上动态规划求出最长链。
至于实现,开始建图可以用\(Map\)的方式存下所有有用的点,对行和列分别建立虚点,前两种门就分别只与对应行和列的虚点连边,第三种门直接扫描周围八个点是否存在进行连边。这样做的话,\(Tarjan\)缩点的时候就要注意不要把虚点统计进\(Size\),需要判断一下。
然后在新图上建边,注意这里最好用\(Set\)标记一下,以防建太多的重复边。

代码

//这份代码在Luogu上有三个点MLE,暂未改出
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;

#define ll long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr))

const int maxN=100001*3;
const int maxMap=1000001;
const int maxM=maxN*8;
const int F1[9]={0,-1,-1,-1,0,1,1,1,0};
const int F2[9]={0,-1,0,1,1,1,0,-1,-1};
const int inf=2147483647;

int n,R,C;
int Ans=0;
int nodecnt=0,nnodecnt;
int edgecnt=-1,Head[maxN],Next[maxM],V[maxM];
map<int,int> Map[maxMap],Opt[maxMap];
int dfncnt=0,dfn[maxN],low[maxN],Id[maxN];

int idcnt=0,Size[maxN];
int stacktop=0,Stack[maxN];
bool instack[maxN];
int edgecnt2=0,Head2[maxN],Next2[maxN*2],V2[maxN*2],Indegree[maxN*2];
int Queue[maxN],F[maxN];
int Hang[maxMap],Lie[maxMap];
set<int> Set[maxN];

void Add_Edge(int u,int v);
void Add_Edge2(int u,int v);
void dfs(int u,int fa);
int GetHang(int h);
int GetLie(int l);

int main()
{
    ios::sync_with_stdio(false);
    mem(Head,-1);mem(Head2,-1);
    cin>>n>>R>>C;
    for (int i=1;i<=n;i++)
    {
        int x,y,opt;cin>>x>>y>>opt;
        Map[x][y]=++nodecnt;Opt[x][y]=opt;//Opt记录这个宫室的类型
    }
    nnodecnt=nodecnt;
    for (int i=1;i<=R;i++)
        for (map<int,int>::iterator j=Map[i].begin();j!=Map[i].end();j++)//建图
        {
            Add_Edge(GetHang(i),j->second);Add_Edge(GetLie(j->first),j->second);//首先从对应的行和列连边过来
            if (Opt[i][j->first]==1) Add_Edge(j->second,GetHang(i));//第一种门
            else if (Opt[i][j->first]==2) Add_Edge(j->second,GetLie(j->first));//第二种门
            else for (int f=1;f<=8;f++) if (Map[i+F1[f]].count(j->first+F2[f])!=0) Add_Edge(j->second,Map[i+F1[f]][j->first+F2[f]]);//第三种门
        }
    for (int i=1;i<=nodecnt;i++)//Tarjan缩点
        if (dfn[i]==0) dfs(i,i);
    for (int i=1;i<=nodecnt;i++)//建立缩完点后的新图
        for (int j=Head[i];j!=-1;j=Next[j])
            if ((Id[i]!=Id[V[j]])&&(Set[i].count(V[j])==0))
                Add_Edge2(Id[i],Id[V[j]]),Set[i].insert(V[j]);
    int h=0,t=0;//Top序动态规划求最长链
    for (int i=1;i<=idcnt;i++) if (Indegree[i]==0) Queue[++h]=i;
    do
    {
        int u=Queue[++t];
        for (int i=Head2[u];i!=-1;i=Next2[i])
        {
            F[V2[i]]=max(F[V2[i]],F[u]+Size[u]);
            Indegree[V2[i]]--;if (Indegree[V2[i]]==0) Queue[++h]=V2[i];
        }
    }
    while (t!=h);
    for (int i=1;i<=idcnt;i++) Ans=max(Ans,F[i]+Size[i]);//取最优解
    printf("%d\n",Ans);
    return 0;
}

void Add_Edge(int u,int v)
{
    edgecnt++;Next[edgecnt]=Head[u];Head[u]=edgecnt;V[edgecnt]=v;
    return;
}

void Add_Edge2(int u,int v)
{
    edgecnt2++;Next2[edgecnt2]=Head2[u];Head2[u]=edgecnt2;V2[edgecnt2]=v;
    Indegree[v]++;
    return;
}

void dfs(int u,int fa)//Tarjan
{
    dfn[u]=low[u]=++dfncnt;
    Stack[++stacktop]=u;instack[u]=1;
    for (int i=Head[u];i!=-1;i=Next[i])
    {
        if (dfn[V[i]]==0){
            dfs(V[i],u);low[u]=min(low[u],low[V[i]]);
        }
        else if (instack[V[i]]) low[u]=min(low[u],dfn[V[i]]);
    }
    if (dfn[u]==low[u]){
        int size=0,v;idcnt++;
        do{
            v=Stack[stacktop--];Id[v]=idcnt;instack[v]=0;if (v<=nnodecnt) size++;//注意这里if一句,是保证只有实点才被统计进Size中
        }
        while (u!=v);
        Size[idcnt]=size;
    }
    return;
}

int GetHang(int h)
{
    if (Hang[h]==0) Hang[h]=++nodecnt;
    return Hang[h];
}

int GetLie(int l)
{
    if (Lie[l]==0) Lie[l]=++nodecnt;
    return Lie[l];
}

本文章由SYCstudio或本站其它作者所有,请勿擅自转载

本文链接地址:[BZOJ1924/Luogu2403][Sdoi2010]所驼门王的宝藏(Tarjan,动态规划)


HNCJ OIer