• 关于我想了很久才想出这题咋做这档事


    #0.0 目录


    目录中以下题目皆可点击查看原题

    #1.0 P2004 领地选择

    不难想到,这里就是让在一个有 (N imes M) 个点的矩形中,找到有 (C imes C) 个点的正方形,使各点之和最大,那么很自然的就会想到二维前缀和
    我们在读入点 ((i,j))时,第 (i-1) 行的数以及点 ((i,j)) 左边的数都已被读入,所以我们可以在一个点输入时便算出他的前缀和,那么他的前缀和怎么算?如图:

    这张图就知道了 ((i,j)) 可以由 ((i-1,j))((i,j-1)) 两块构成,不过要注意两个点

    • 有一块矩阵我们重复加了,也就是 ((i-1,j-1)) 这一块,所以我们要减去它
    • 我们这个矩阵是不完整的,由图可知我们还有一块深蓝色的没有加,也就是 ((i,j)) 这一点,所以我们要再加上该点的值
      该点前缀和即为 DP[i][j]=DP[i-1][j]+DP[i][j-1]-DP[i-1][j-1]+map[i][j]

    如下图,我们可以凭借前缀和算出以 ((i,j)) 为正方形右下角时,所能得到的各点之和

    S=DP[i][j]-DP[i-c][j]-DP[i][j-c]+dp[j-c][i-c],这里原理与上面相同,不过是将 (1) 变成了 (c)
    之后比较所得答案大小,对现有答案进行更新,时间复杂度为 (O(n cdot m))

    #include <iostream>
    #include <cstdio>
    #define SIZE 10010
    using namespace std;
    
    int n,m,c;
    int ma[SIZE][SIZE],ans[3];
    
    int main(){
        ans[0] = -0x3fffffff;
        scanf("%d%d%d",&n,&m,&c);
        for (int i = 1;i <= n;++ i)
          for (int j = 1;j <= m;++ j){
              scanf("%d",&ma[i][j]);
              ma[i][j] = ma[i - 1][j] + ma[i][j - 1] - ma[i - 1][j - 1] + ma[i][j];
              if (i >= c && j >= c){
                  int s = ma[i][j] - ma[i - c][j] - ma[i][j - c] + ma[i - c][j - c];
                  if (ans[0] < s){
                      ans[0] = s;
                      ans[1] = i - c + 1;
                      ans[2] = j - c + 1;
                  }
              }
          }
        printf("%d %d",ans[1],ans[2]);
        return 0;
    }
    

    #2.0 P3916 图的遍历

    最初的想法是dfs加记忆化,但遗憾并没有实现,在计算时会遇到错误,所以换一个思路。
    我们要找从点 (v) 出发能到达的编号最大的点,显然,这个最大编号的点到点 (v) 之间显然是有路可走的,正着来不行,那么我们可否可以从反方向建图,从编号大的点 (u) 开始dfs,能遍历到的点所能达到的最大的点显然就是这个点 (u),对每个点进行标记,后面再次遍历时便不会再进行更新(显然后面的遍历起始点 (i) 的编号小于当前点 (u) 的编号),由于每个点只遍历 (1) 次,故时间复杂度为 (O(n))

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define SIZE 100011
    using namespace std;
    
    struct Edge{
        int to;
        int next;
    };
    Edge e[SIZE];
    
    int n,m,tot;
    int head[SIZE],vis[SIZE],ans,sum[SIZE];
    
    inline void add(int v,int u){
        e[tot].to = v;
        e[tot].next = head[u];
        head[u] = tot;
        tot ++;
    }
    
    inline void dfs(int p,int st){
        if (sum[p])
          return;
        sum[p] = st;
        for (int j = head[p];j != -1;j = e[j].next){
    	if (!sum[e[j].to])
              dfs(e[j].to,st);
        }  
    }
    
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for (int i = 0;i < m;i ++){
    	int u,v;
    	scanf("%d%d",&u,&v);
    	add(u,v);
        }
        for (int i = n;i > 0;i --)
          dfs(i,i);
        for (int i = 1;i <= n;i ++)
          printf("%d ",sum[i]);
        return 0;
    }
    

    #3.0 P4017 最大食物链计数

    看题面描述,不难想到以下几点:

    • 这是个有向无环图DAG
    • 我们要从入度为零的点开始查找
    • 每次要等一个点的入度为零后才可以将它入队,
    • 我们要求从所有入度为零的点到出度为零的点总路径数

    显然,我们要用 拓扑排序 来做
    假设有以下一个图:

    由入度为零的点到点A有 (a) 条路径
    由入度为零的点到点B有 (b) 条路径
    根据加法原理,由入度为零的点到点C有 (a+b) 条路径

    思路并不难,将入度为零的点入队,
    之后对队头的点 (i) 进行遍历,同时将点 (i) 可达到的点 (j) 的入度减一,点 (j) 的答案数加上点 (i) 的答案数;如果点 (j) 的入度为零,将该点入队

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define SIZE 500010
    #define Mod 80112002
    using namespace std;
    
    struct Edge{
        int from;
        int to;
        int next;
    };
    Edge e[SIZE];
    
    int n,m,ans,tot;
    int head[SIZE],out[SIZE],in[SIZE],sum[SIZE];
    
    queue <int> q;
    
    inline void add(int u,int v){
        e[tot].to = v;
        e[tot].next = head[u];
        head[u] = tot;
        in[v] ++;
        out[u] ++;
        tot ++;
    }
    
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for (int i = 1;i <= m;i ++){
    	int u,v;
    	scanf("%d%d",&u,&v);
    	add(u,v);
        }
        for (int i = 1;i <= n;i ++)
          if (in[i] == 0){
    	  sum[i] = 1;
    	  q.push(i);
          }
        while (!q.empty()){
    	int now = q.front();
    	q.pop();
    	for (int i = head[now];i != -1;i = e[i].next){
    	    sum[e[i].to] += sum[now];
    	    sum[e[i].to] %= Mod;
    	    in[e[i].to] --;
    	    if (!in[e[i].to])
    	      if (!out[e[i].to]){
    		ans += sum[e[i].to];
    		ans %= Mod;
    	      }
    	    else
    	      q.push(e[i].to);
    	}
        }
        printf("%d",ans);
        return 0;
    }
    

    #4.0 P1038 神经网络

    首先,根据题面,我们可以知道以下几点:

    • 每个图都是一个有向无环图DAG
    • (C_i) 的计算公式为 (C_i=sumlimits_{(j,i)in E}W_{ji}C_j-U_i),其中 ((j,i)in E) 为所有以 (i) 为终点的边,(W_{ji}) 为这条边的权值,(U_i) 为阈值
    • 要从入度为 (0) 的点开始向后计算,点 (i)(C_i) 要算出后才可以向后传递(注意只有当 (C_i>0) 时才会后传)
    • 我们要求每个出度为 (0) 的点的 (C)

    关于第2点的公式,我们可以这么理解,每遍历到一条以 (i) 为终点的边,对 (C_i) 进行一次更新,然后使点 (i) 的入度减一,当点 (i) 的入度为 (0) 后,便可以用它向下传递信息,这样也同时满足了第三点
    综上,我们很容易想到用 拓扑排序 来做这道题

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #define SIZE 100010
    using namespace std;
    
    struct Node{ //每个点拥有的数值
        int c;
        int thre;
        int in;
        int out;
    };
    Node d[SIZE];
    
    struct Edge{ //每条边拥有的数值
        int u;
        int v;
        int w;
        int next;
    };
    Edge e[SIZE];
    
    queue <int> q;
    
    int n,p,head[SIZE],tot,f;
    
    inline void add(int u,int v,int w){ //加边,记得对点的入度和出度进行更新
        e[tot].u = u;
        e[tot].v = v;
        e[tot].w = w;
        e[tot].next = head[u];
        head[u] = tot;
        tot ++;
        d[u].out ++;
        d[v].in ++;
    }
    
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&p);
        for (int i = 1;i <= n;i ++){
    	scanf("%d%d",&d[i].c,&d[i].thre);
    	if (d[i].c > 0)//只有输入层的点一开始就有状态,即为入度为0的点
    	  q.push(i);
    	else
    	  d[i].c -= d[i].thre; //阈值是迟早要减掉的,但输入层也有可能有阈值但不用减掉
        }  
        for (int j = 1;j <= p;j ++){
    	int u,v,w;
    	scanf("%d%d%d",&u,&v,&w);
    	add(u,v,w);
        }
        while (!q.empty()){
    	int now = q.front();
    	q.pop();
    	if (d[now].c <= 0) //只有C大于0才会后传信息
    	  continue;
    	for (int i = head[now];i != -1;i = e[i].next){ //边的遍历
        	    d[e[i].v].c += e[i].w * d[now].c; //更新终点的C值与入度
    	    d[e[i].v].in --;
    	    if (!d[e[i].v].in) //当该点入度为0时开始后传信息
    	      q.push(e[i].v);
    	}
        }
        for (int i = 1;i <= n;i ++) //注意输出格式
          if (!d[i].out && d[i].c > 0){
      	f = 1;
      	printf("%d %d
    ",i,d[i].c);
          }
        if (!f)
          printf("NULL");
        return 0;
    }
    

    更新日志及说明

    更新

    • 初次完成编辑 - (2020.10.17)
    • 增添了 #4.0 P1038 神经网络 的内容
      本文若有更改或补充会持续更新

    个人主页

    欢迎到以下地址支持作者!
    Github戳这里
    Bilibili戳这里
    Luogu戳这里

  • 相关阅读:
    asp.net过滤数据中有异常数据字符串
    微信内置浏览器的 User Agent的判断
    最近突然想了很久还是开博每天写点什么
    Sonar-scanner 插件配置应用
    存clob的值
    动态代理
    在oracle函数中不可直接将变量作为sql语句中的参数
    按照行、列进行统计(按两个维度进行统计)
    查询关联不上的数据,三张表查询
    前台页面——js/jq循环
  • 原文地址:https://www.cnblogs.com/Dfkuaid-210/p/13828176.html
Copyright © 2020-2023  润新知