题解 P3627 【[APIO2009]抢掠计划】

    这道题前面的思路和其他题解一样,都是tarjan缩点。然后是要求金币的最大值,我用的是记忆化搜索,用jiyi[i]来表示从编号为i的联通块出发,最多可以抢到多少钱,搜索时如果遇到已经搜过的点(知道从那个点开始最多能抢多少钱)就直接return。因为记忆化搜索每个点只会遍历一次,所以记忆化搜索的复杂度应该是O(n)。

    另外我的码风有点奇怪,先解释一下:stk[tk]分别是栈和栈的指针(tarjan用),num表示联通块个数,tim时间戳,shuyu[i]表示点i属于哪个联通块,he[]表示某个联通块内总共有多少钱,vis[]表示一个点是否在栈中,jiuba[]表示某个点是否有酒吧,suojb[]表示缩点后的连通块中是否有酒吧。bian[],head[],ecnt都是缩点前图中的边,如果后面有后缀x,则表示缩点后新图中的边;

    1. #include<bits/stdc++.h>
    2. using namespace std;
    3. const int N=500005;
    4. int n,m,ecnt,s,p,tk,num,tim,ecntx,ans;
    5. int qian[N],head[N],dfn[N],low[N],stk[N],shuyu[N],he[N],headx[N],jiyi[N];
    6. bool vis[N],jiuba[N],suojb[N];
    7. struct aaa
    8. {
    9. int to,nxt;
    10. }bian[N],bianx[N];
    11. inline int read()//标准快读
    12. {
    13. int x=0;bool f=0;char c=getchar();
    14. while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
    15. while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    16. return f?-x:x;
    17. }
    18. inline void add(int a,int b)//链式前向星加边;
    19. {
    20. bian[++ecnt].to=b;
    21. bian[ecnt].nxt=head[a];
    22. head[a]=ecnt;
    23. }
    24. inline void addx(int a,int b)//缩点后联通块与联通块加边;
    25. {
    26. bianx[++ecntx].to=b;
    27. bianx[ecntx].nxt=headx[a];
    28. headx[a]=ecntx;
    29. }
    30. void tarjan(int u)//标准tarjan
    31. {
    32. dfn[u]=low[u]=++tim;
    33. stk[++tk]=u;vis[u]=1;
    34. for(int i=head[u];i;i=bian[i].nxt)
    35. {
    36. int v=bian[i].to;
    37. if(!dfn[v])
    38. tarjan(v),low[u]=min(low[u],low[v]);
    39. else if(vis[v])low[u]=min(low[u],low[v]);
    40. }
    41. if(dfn[u]==low[u])
    42. {
    43. num++;
    44. while(stk[tk]!=u)
    45. {
    46. shuyu[stk[tk]]=num;//记录点属于的联通块;
    47. vis[stk[tk]]=0;
    48. stk[tk]=0;tk--;
    49. }
    50. shuyu[stk[tk]]=num;//此时栈顶还有一个点在此联通块中,再执行一次
    51. vis[stk[tk]]=0;
    52. stk[tk]=0;tk--;
    53. }
    54. }
    55. void suo()//缩点;
    56. {
    57. for(int i=1;i<=n;i++)
    58. {
    59. he[shuyu[i]]+=qian[i];//更新每个联通块内的总钱数
    60. if(jiuba[i])suojb[shuyu[i]]=1;更新联通块内是否有酒吧
    61. for(int j=head[i];j;j=bian[j].nxt)
    62. {
    63. int v=bian[j].to;
    64. if(shuyu[i]!=shuyu[v])
    65. addx(shuyu[i],shuyu[v]);//如果起点和终点不在同一个联通块,就在这两个联通块之间加边;
    66. }
    67. }
    68. }
    69. int dfs(int dian)//缩了点,新图为有向无环图,此时才能dfs
    70. {
    71. if(jiyi[dian])return jiyi[dian];//如果这个联通块已经被搜索过,即我们知道从这个点开始的最大钱数,就直接返回;
    72. if(headx[dian]==0&&suojb[dian]==1)//如果这个联通块出度为0并且此处有酒吧,就可以在此处停止,返回这个联通块的总钱数;
    73. {
    74. jiyi[dian]=he[dian];
    75. return he[dian];
    76. }
    77. if(headx[dian]==0&&suojb[dian]==0)return 0;//如果这个联通块出度为0但是没有酒吧,就不能停止在此处,所以它对答案的贡献是0;
    78. for(int i=headx[dian];i;i=bianx[i].nxt)
    79. {
    80. jiyi[dian]=max(jiyi[dian],dfs(bianx[i].to));//一个联通块的多个出度中取一个最大的;
    81. }
    82. if(jiyi[dian]==0&&suojb[dian]==0)return 0;
    83. //注意:我们此时少考虑了一种情况,如果一条路径上一连串的点都没有酒吧,那么劫匪是不可以在这条路径上停下的,但如果没了这句话,程序还是会走这条路;加上这句话,如果后面的点没有酒吧 并且 此处也没有酒吧,那么它对答案的贡献是0,直接return 0;
    84. jiyi[dian]+=he[dian];//把自己的点权加进记忆化数组中;
    85. return jiyi[dian];//返回从这点开始的最大钱数;
    86. }
    87. int main()
    88. {
    89. int a,b;
    90. n=read();m=read();
    91. for(int i=1;i<=m;i++)
    92. {
    93. a=read();b=read();
    94. add(a,b);
    95. //有个奇怪的错误,不能写add(read(),read());否则会出错,我也不知道为什么;
    96. }
    97. for(int i=1;i<=n;i++)
    98. qian[i]=read();
    99. s=read();p=read();
    100. for(int i=1;i<=p;i++)
    101. jiuba[read()]=1;
    102. for(int i=1;i<=n;i++)
    103. if(!dfn[i])
    104. tarjan(i);
    105. suo();
    106. ans=dfs(shuyu[s]);
    107. cout<<ans;
    108. return 0;
    109. }

      

    转载于:https://www.cnblogs.com/oierjzy/p/11229466.html

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值

    举报

    选择你想要举报的内容(必选)
    • 内容涉黄
    • 政治相关
    • 内容抄袭
    • 涉嫌广告
    • 内容侵权
    • 侮辱谩骂
    • 样式问题
    • 其他
    点击体验
    DeepSeekR1满血版
    程序员都在用的中文IT技术交流社区

    程序员都在用的中文IT技术交流社区

    专业的中文 IT 技术社区,与千万技术人共成长

    专业的中文 IT 技术社区,与千万技术人共成长

    关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

    关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

    客服 返回顶部

    登录后您可以享受以下权益:

    ×