• 洛谷P3183食物链题解


    不得不说,这是道很难减少时间复杂度的题,且这个题有点像一道拓扑排序题,但是这个难度标签有点低。

    我们应该可以想到拓扑排序可能是这个题的正解,但是题目中有输出总数,因此我们就可以造一个数组表示从这个点出发向下有几个食物链,然后最后再输出每个入度为零且出度不为零的点所记忆化搜索到的点的个数。

    我们先上70分的代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    struct zzz{
        int t,nex;
    }e[200010<<1]; int head[100010],tot;
    void add(int x,int y){
        e[++tot].t=y;
        e[tot].nex=head[x];
        head[x]=tot;
    }
    int in[100010],out[100010],vis[100010],ans;
    int dfs(int x){
        if(vis[x]) 
        return vis[x];
        for(int i=head[x];i;i=e[i].nex){
            if(out[e[i].t]==0)
              vis[e[i].t]++;
            else vis[x]+=dfs(e[i].t);
        }
    }
    int main(){
        int n,m; scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,y; scanf("%d%d",&x,&y);
            in[y]++; out[x]++;
            add(x,y);
        }
        for(int i=1;i<=n;i++){
            if(in[i]==0&&out[i]){
                  dfs(i);
              }
            ans+=vis[i];
        }
        cout<<ans;
        return 0;
    }

    我们看这个代码觉得这不是加记忆化搜索了吗,为什么还是不够快呢。

    因为这个记忆化他只加了一个,就是第一个,可是万一这一个始终实现不了,那就GG了,所以我们应该再加两个,即在每搜索到一个出度为0的点就返回1,且我们应该在遍历完这个点的所有边的时候加完之后再return一个。

    所以做完这个题我们应该有了一个教训,便是记忆化搜索能加就加,别吝啬。

    代码;

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    struct cym{
        int u;
        int v;
        int next;
    }e[200100];
    int head[100100],in_degree[100001],out_degree[100010],ans,total,dp[100010];//dp数组表示i这个点向下有几个出度为零的点,也就是有几条食物链 
    void add(int a,int b)
    {
        e[++total].u=a;
        e[total].v=b;
        e[total].next=head[a];
        head[a]=total;
    }
    int dfs(int x)
    {
        int num=0;
        if(dp[x])
        return dp[x];
        if(!out_degree[x])
        return 1;//如果出度为零,就直接加1;
        for(int i=head[x];i;i=e[i].next)
        num+=dfs(e[i].v);//向下不断遍历,加上以x为起点的边其终点的dp数组的值 
        dp[x]=num;
        return dp[x];
    }
    int main()
    {
        int n,m,u,v;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            in_degree[v]++;
            out_degree[u]++;//记录出入度,别记反了。 
        }
        for(int i=1;i<=n;i++)
          if(!in_degree[i]&&out_degree[i])
            ans+=dfs(i);//有几个入度为零的点就算几次食物链,比如样例就只算算了一次 
        cout<<ans;
     } //@4017

    代码后面有彩蛋。

  • 相关阅读:
    跨DLL边界传递CRT对象的隐患(或诸如:HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)问题出现的原因)
    【策略模式】不同的时间用不同的规则优先考虑策略模式
    【装饰模式】遵循开闭原则,使用一个类装饰另一个类
    【简单的工厂模式】一个简单的计算器
    【原】使用Golang语言编写echo程序
    WebBrowser或CHtmlView中轻松屏蔽脚本错误(JavaScript)
    春运买票难,是谁造成的?
    搜狗输入法使用感受
    [原]在 go/golang语言中使用 google Protocol Buffer
    防护针对SQL Server数据库的SQL注入攻击
  • 原文地址:https://www.cnblogs.com/liuwenyao/p/9236097.html
Copyright © 2020-2023  润新知