题解-路径数+算法-回荡dp

Description

题目背景
Euphemia\texttt{Euphemia}Euphemia 到一个 N×NN\times NN×N 的药草田里采药,她从左上角的格子田(第一行,第一列)出发,要到达右下角(第 NNN 行,第 NNN 列)的格子田,每次她可以走到与当前格子有边相邻的格子去,但她不会走已经走过的格子,而且出于对美的要求,她走过的路径是关于 左下-右上 对角线对称的。由于地势不同,在每个格子田采药都会有一个疲劳度 Ti,jT_{i,j}Ti,j​,Euphemia\texttt{Euphemia}Euphemia 想知道:有多少条合法路径,可以使得她采药的疲劳度最小。
输入格式:
多组测试数据。
每组数据第一行一个整数 NNN,接下来 NNN 行,每行 NNN 个非零数字(1,2,3...91,2,3...91,2,3...9 中一个),表示格子田的疲劳度。
当 N=0N=0N=0,输入结束。
输出格式:
对于每组数据,输出一个整数表示答案,答案%1000000009\%1000000009%1000000009。
样例输入:

2
1 1
1 1
3
1 1 1
1 1 1
2 1 1
0

样例输出:

2
3

数据范围:
对于 20%20\%20% 的数据满足 N≤5N\le5N≤5。
对于另外 20%20\%20% 的数据满足 N≤40N\le40N≤40。
对于 100%100\%100% 的数据满足 N≤100N\le100N≤100,不超过 505050 组数据。


Introduction

这题很容易骗分:

如果用 dfs\texttt{dfs}dfs 暴力枚举路径骗分,可以骗到 20分\color{#f34c05}\texttt{20分}20分。

如果用错误的思路 Θ(n2)\Theta(n^2)Θ(n2) 来 dp\texttt{dp}dp,也能骗到 60分\color{#ffcc00}\texttt{60分}60分(只往右下角走,不回头)。

Solution

先找路径最小权值

首先因为要从左下角走到右下角并且路线根据左下-右上的对角线对称,所以相对于每个格子的权值为:

vali,j={Ti,j+TN−j+1,N−i+1(i+j<N+1)Ti,j(i+j==N+1)val_{i,j}= \begin{cases} T_{i,j}+T_{N-j+1,N-i+1}(i+j<N+1)\\ T_{i,j}(i+j==N+1) \end{cases} vali,j​={Ti,j​+TN−j+1,N−i+1​(i+j<N+1)Ti,j​(i+j==N+1)​

然后对于 i+j>N+1i+j>N+1i+j>N+1 的格子就不用考虑了,只需要考虑从 (1,1)(1,1)(1,1) 左上角走到 i+j==N+1i+j==N+1i+j==N+1 的格子的最小权值路径即可。


找最短路径要用到的算法是:回荡 dp\texttt{dp}dp。

因为该算法是本蒟蒻考场上急中生智想出来的,所以就给它取了个奇怪的名字。

因为题目并没有说只能像下-左走,所以可能出现如下恶心数据:

10
1 1 1 1 1 9 9 9 9 9
9 9 9 9 1 9 9 9 9 9
9 9 1 1 1 9 9 9 9 9
9 9 1 9 9 9 9 9 9 9
9 9 1 1 1 1 9 9 9 9
9 9 9 9 9 1 9 1 1 1
9 9 9 9 9 1 9 1 9 1
9 9 9 9 9 1 1 1 9 1
9 9 9 9 9 9 9 9 9 1
9 9 9 9 9 9 9 9 9 1
0

这时的最小权值路径应该是沿着图中的 111 走。如果用普通的 dp\texttt{dp}dp,就会很难找到一个合适的递推顺序。

但是因为它这样拐的弯最多只有 nnn 个,所以可以考虑这样做:

重复 nnn 次

从左上角到对称轴顺序 dp\texttt{dp}dp。
从对称轴到左上角逆序 dp\texttt{dp}dp。

这样就保证可以覆盖到任何路径了。记 fi,j(i+j≤N+1)f_{i,j}(i+j\le N+1)fi,j​(i+j≤N+1) 表示 (1,1)(1,1)(1,1) 到 (i,j)(i,j)(i,j) 的路径最小权值,所以这里可以有一个小的优化,如果一次正序和一次逆序 dp\texttt{dp}dp 都没有改变任何 fi,jf_{i,j}fi,j​ 的值,则停止重复正序逆序回荡 dp\texttt{dp}dp。

Code:

//...
bool zxf(){//左上角到对称轴bool res=0;for(int i=3;i<=n+1;i++)for(int x=1;x<i;x++){int y=i-x;if(min(f[x][y-1],f[x-1][y])+val(x,y)<f[x][y])f[x][y]=min(f[x][y-1],f[x-1][y])+val(x,y),res=1;}return res;
}
bool fxf(){//对称轴到左上角bool res=0;for(int i=n;i>=2;i--)for(int x=1;x<i;x++){int y=i-x;if(min(f[x][y+1],f[x+1][y])+val(x,y)<f[x][y])f[x][y]=min(f[x][y+1],f[x+1][y])+val(x,y),res=1;}return res;
}
//...
void dp(){//...memset(f,0x3f,sizeof f);f[1][1]=val(1,1);for(int i=1;i<=n;i++)if(!zxf()&&!fxf()) break;//空回荡优化//...
}
//...

然后统计最小权值路径数

思路类似,记 gi,j(i+j≤N+1)g_{i,j}(i+j\le N+1)gi,j​(i+j≤N+1) 表示从 (i,j)(i,j)(i,j) 到对称轴 (x,y)(x+y==N+1)(x,y)(x+y==N+1)(x,y)(x+y==N+1) 上最小权值路径的条数。

首先找到 mn=min⁡{fi,j}(i+j==N+1)mn=\min\{f_{i,j}\}(i+j==N+1)mn=min{fi,j​}(i+j==N+1)。找到所有满足 fi,j(i+j==N+1)==mnf_{i,j}(i+j==N+1)==mnfi,j​(i+j==N+1)==mn 的 (i,j)(i,j)(i,j),令 gi,j=1g_{i,j}=1gi,j​=1。然后因为最短路径也会是绕来绕去的,所以再次回荡 dp\texttt{dp}dp 找最短路线经过的格子:

重复 nnn 次

从对称轴到左上角正序逆推 dp\texttt{dp}dp。
从左上角到对称轴逆序逆推 dp\texttt{dp}dp。

然后因为这里不是求最小值,容易重复计算路径,所有应用类似网络流的思想,记 fwi,j,k(i+j≤N+1,k∈{0,1,2,3})fw_{i,j,k}(i+j\le N+1,k\in\{0,1,2,3\})fwi,j,k​(i+j≤N+1,k∈{0,1,2,3}) 表示 (i,j)(i,j)(i,j) 这个格子在 kkk 方向上已经递推了的 ggg 值,如果新一轮回荡中所有 fwi,j,kfw_{i,j,k}fwi,j,k​ 的值都没有改变,就优化——停止回荡。

Code:

//...
bool zxg(){//从左上角到对称轴逆序逆推dpbool res=0;for(int i=3;i<=n+1;i++)for(int x=1;x<i;x++){int y=i-x;if(f[x][y]+val(x-1,y)==f[x-1][y]&&g[x-1][y]>fw[x][y][0])(g[x][y]+=g[x-1][y]-fw[x][y][0])%=mod,fw[x][y][0]=g[x-1][y],res=1;if(f[x][y]+val(x,y-1)==f[x][y-1]&&g[x][y-1]>fw[x][y][1])(g[x][y]+=g[x][y-1]-fw[x][y][1])%=mod,fw[x][y][1]=g[x][y-1],res=1;}return res;
}
bool fxg(){//从对称轴到左上角正序逆推dp。bool res=0;for(int i=n;i>=2;i--)for(int x=1;x<i;x++){int y=i-x;if(f[x][y]+val(x+1,y)==f[x+1][y]&&g[x+1][y]>fw[x][y][2])(g[x][y]+=g[x+1][y]-fw[x][y][2])%=mod,fw[x][y][2]=g[x+1][y],res=1;if(f[x][y]+val(x,y+1)==f[x][y+1]&&g[x][y+1]>fw[x][y][3])(g[x][y]+=g[x][y+1]-fw[x][y][3])%=mod,fw[x][y][3]=g[x][y+1],res=1;}return res;
}
void dp(){//...mn=inf;for(int i=1;i<=n;i++)mn=min(mn,f[i][n+1-i]);memset(g,0x00,sizeof g);for(int i=1;i<=n;i++)if(f[i][n+1-i]==mn) g[i][n+1-i]=1;memset(fw,0x00,sizeof fw);for(int i=1;i<=n;i++)if(!fxg()&&!zxg()) break;//...
}
//...

最后答案就是 g1,1g_{1,1}g1,1​,即 (1,1)(1,1)(1,1) 到对称轴上最小权值路径格子的路径数。即 (1,1)(1,1)(1,1) 到 (N,N)(N,N)(N,N) 的最小 Ti,jT_{i,j}Ti,j​ 和路径条数。时间复杂度是 Θ(N3)\Theta(N^3)Θ(N3),如果有优化,应该 N=1000N=1000N=1000 的数据也过得了。

Code

#include <bits/stdc++.h>
using namespace std;//@Start
const int inf=0x3f3f3f3f;//@Debug
void debug(int x,int y,int arr[][1010]){for(int i=1;i<=x;i++)for(int j=1;j<=y;j++)printf("%d%c",arr[i][j],"\n "[j<y]);
}//@DP
const int N=1010,mod=1e9+9;
int n,a[N][N],f[N][N],g[N][N],mn,fw[N][N][4];
int val(int x,int y){if(x+y==n+1) return a[x][y];return a[x][y]+a[n-y+1][n-x+1];
}
bool zxf(){bool res=0;for(int i=3;i<=n+1;i++)for(int x=1;x<i;x++){int y=i-x;if(min(f[x][y-1],f[x-1][y])+val(x,y)<f[x][y])f[x][y]=min(f[x][y-1],f[x-1][y])+val(x,y),res=1;}return res;
}
bool fxf(){bool res=0;for(int i=n;i>=2;i--)for(int x=1;x<i;x++){int y=i-x;if(min(f[x][y+1],f[x+1][y])+val(x,y)<f[x][y])f[x][y]=min(f[x][y+1],f[x+1][y])+val(x,y),res=1;}return res;
}
bool zxg(){bool res=0;for(int i=3;i<=n+1;i++)for(int x=1;x<i;x++){int y=i-x;if(f[x][y]+val(x-1,y)==f[x-1][y]&&g[x-1][y]>fw[x][y][0])(g[x][y]+=g[x-1][y]-fw[x][y][0])%=mod,fw[x][y][0]=g[x-1][y],res=1;if(f[x][y]+val(x,y-1)==f[x][y-1]&&g[x][y-1]>fw[x][y][1])(g[x][y]+=g[x][y-1]-fw[x][y][1])%=mod,fw[x][y][1]=g[x][y-1],res=1;}return res;
}
bool fxg(){bool res=0;for(int i=n;i>=2;i--)for(int x=1;x<i;x++){int y=i-x;if(f[x][y]+val(x+1,y)==f[x+1][y]&&g[x+1][y]>fw[x][y][2])(g[x][y]+=g[x+1][y]-fw[x][y][2])%=mod,fw[x][y][2]=g[x+1][y],res=1;if(f[x][y]+val(x,y+1)==f[x][y+1]&&g[x][y+1]>fw[x][y][3])(g[x][y]+=g[x][y+1]-fw[x][y][3])%=mod,fw[x][y][3]=g[x][y+1],res=1;}return res;
}
void dp(){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);memset(f,0x3f,sizeof f);f[1][1]=val(1,1);for(int i=1;i<=n;i++)if(!zxf()&&!fxf()) break;// debug(n,n,f);mn=inf;for(int i=1;i<=n;i++)mn=min(mn,f[i][n+1-i]);memset(g,0x00,sizeof g);for(int i=1;i<=n;i++)if(f[i][n+1-i]==mn) g[i][n+1-i]=1;memset(fw,0x00,sizeof fw);for(int i=1;i<=n;i++)if(!fxg()&&!zxg()) break;printf("%d\n",g[1][1]);
}//@Main
int main(){// freopen("100.in","r",stdin);scanf("%d",&n);while(n) dp(),scanf("%d",&n);//多组测试数据return 0;
}

祝大家学习愉快!

题解-路径数+算法-回荡dp相关推荐

  1. leetcode算法题--出界的路径数★

    原题链接:https://leetcode-cn.com/problems/out-of-boundary-paths/ 动态规划 dp[i][j][k]保存的是在(i,j)这个点,步数为k的所有路径 ...

  2. C语言求网格的最大不重复路径数的算法(附完整源码)

    C语言求网格的最大不重复路径数的算法 C语言求网格的最大不重复路径数的算法完整源码(定义,实现,main函数测试) C语言求网格的最大不重复路径数的算法完整源码(定义,实现,main函数测试) #in ...

  3. 算法题-----题目、题解、个人算法、个人思考

    算法题-----题目.题解.个人算法.个人思考,项目说明]这里记录算法题目,这里记录:算法题目简单描述,暂存对于这道题的SK_cache(自己的卡点.自己的思路.自己对这道题的理解,自己的疑问.自己对 ...

  4. 【牛客每日一题】4.15 Treepath 题解(树上dfs/树形DP)

    题目链接:https://ac.nowcoder.com/acm/problem/14248 来源:牛客网 题目描述 给定一棵n个点的树,问其中有多少条长度为偶数的路径.路径的长度为经过的边的条数.x ...

  5. 基于群智能的路径规划算法(三)------遗传算法

       本系列文章主要记录学习基于群智能的路径规划算法过程中的一些关键知识点,并按照理解对其进行描述和进行相关思考.    主要学习资料是来自 小黎的Ally 的 <第2期课程-基于群智能的三维路 ...

  6. 中国物流供应链“零的突破”!阿里路径规划算法入围运筹学“奥斯卡”

    鱼羊 发自 凹非寺 量子位 报道 | 公众号 QbitAI 最新消息,中国物流力量,刚在全球运筹和管理科学界的最高荣誉中,实现零的突破! 1月15日,国际运筹学与管理科学学会(INFORMS)公布了2 ...

  7. 每天一道LeetCode-----计算从二维数组的左上角到达右下角的所有路径数及最短的那条,如果存在障碍物时又是多少

    Unique Paths 原题链接Unique Paths 计算从左上角有多少条不同的路径可以到达右下角,移动方向只能是向右和向下. 对于每个位置,都有两种移动的可能,即向右移动和向下移动.可以用深度 ...

  8. 576. 出界的路径数

    576. 出界的路径数 给你一个大小为 m x n 的网格和一个球.球的起始坐标为 [startRow, startColumn] .你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格 ...

  9. LeetCode 1786. 从第一个节点出发到最后一个节点的受限路径数(迪杰斯特拉 + 拓扑排序)

    文章目录 1. 题目 2. 解题 1. 题目 现有一个加权无向连通图. 给你一个正整数 n ,表示图中有 n 个节点,并按从 1 到 n 给节点编号:另给你一个数组 edges ,其中每个 edges ...

最新文章

  1. 教你用R语言分析招聘数据,求职/转行不求人~(附代码、数据集)
  2. Mysql一些重要配置参数的学习与整理(一)
  3. 前端人英语学习的那点事儿
  4. PHP 文件上传全攻略
  5. Mono项目的新进展
  6. Python——字典生成式
  7. leetcode 20. 有效的括号 (python)
  8. Windows 7专业版安装VS2005与WinCE6.0开发环境
  9. 设计师:设计师知识储备之硬装部分/软装部分简介、家装材料知识(吊顶材料/门窗材料/五金材料/墙面材料/地面材料/胶粘材料/油漆材料/水电材料/瓦工部分)之详细攻略
  10. 高新技术企业认定专项审计报告包含报告?专审收费标准
  11. VSCode全局搜索快捷键失效
  12. 索引超出数组元素的数目(1)
  13. 为何要从用户角度出发来思考问题
  14. Python - 下载视频网站的视频
  15. VUEcli3设置页签图标
  16. elastic:Another Kibana instance appears to be migrating the index
  17. 量化交易——传统技术分析能量潮指标OBV的原理及实现
  18. C# 如何在Excel中插入上标和下标
  19. 有了这7个高质量的黑科技软件,工作效率翻倍!
  20. linux驱动编写--2--应用程序控制led闪烁

热门文章

  1. JavaScript小练习:制作一个简易的注册页面
  2. 用200行C语言代码写出一个贪吃蛇——1.0(基本版)
  3. 来一个腾讯开平的工具类
  4. 特发性震颤会有哪些影响,日常应该怎么做呢?
  5. 挑战程序设计竞赛(第二章习题总结)
  6. 对ArrayList容器中的数据进行排序
  7. 使用谷歌Cloud Speech API将语音转换为文字
  8. stm32与ps2通信
  9. 5G已经开始布局,云电脑时代即将到来!
  10. tensorflow 自编码器 MNIST数据集