CF24D Broken robot

IL.CF24D Broken robot

DP必须要有方向性。没有明确顺序的DP都是在耍流氓。这就是为什么有“树上DP”和“DAG上DP”而没有“图上DP”,图上有环就不知道应该按什么顺序做了!(像是基环树DP和仙人掌DP都是缩点了,因此顺序还是确定的;环形DP也有“断环成链”的trick)。

那如果真有DP来给你耍流氓怎么办?

还能怎么办?耍回去啊!

例如这题,有两种思路。

  1. 同一行中,转移顺序不定;但是不同行之间,转移顺序还是确定的。因此我们行与行之间以普通的DP转移;同一行中,暴力高斯消元消过去。

我们看一下怎么高斯消元。设有mn列。

则有

fi,j={[j=1]:fi,j+fi1,j+fi,j+13+1[j=n]:fi,j+fi1,j+fi,j13+1otherwise:fi,j+fi1,j+fi,j1+fi,j+14+1

处理一下:

{[j=1]:2fi,jfi,j+1=fi1,j+3[j=n]:2fi,jfi,j1=fi1,j+3otherwise:3fi,jfi,j1fi,j+1=fi1,j+4

这其中,上一行的DP值可以看作是常量。

这样复杂度是O(n4),铁定过不去。

但如果我们把高斯消元的矩阵列出来(5×5)

[2100013100013100013100012]

更大一点:

[210000131000013000000310000131000012]

也就是说,它是一个非常稀疏的矩阵,并且非零元素只分布在主对角线两侧!

在这种特殊矩阵上高斯消元只需要消对角线两侧的位置即可,复杂度是O(n)的。

则总复杂度是O(n2)的。

另外,从点(X,Y)出发走到第n行,可以看作是从第X行的任何点出发,走到点(n,Y)的方案数。

代码:

#include<bits/stdc++.h> 
using namespace std;
int n,m,X,Y;
double f[1010],g[1010][1010];
void Giaos(){
//	for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%lf ",g[i][j]);puts("");}puts("");
	for(int i=1;i<n;i++){
		/*int mp=i;
		for(int j=i+1;j<=min(n,i+2);j++)if(fabs(g[j][i])>fabs(g[mp][i]))mp=j;
		if(mp!=i){
			for(int j=i;j<=min(n,i+2);j++)swap(g[mp][j],g[i][j]);
			swap(g[mp][n+1],g[i][n+1]);
		}
		assert(mp==i);*/
		double tmp=g[i+1][i]/g[i][i];
		g[i+1][i]=0,g[i+1][i+1]-=tmp*g[i][i+1],g[i+1][n+1]-=tmp*g[i][n+1];
	}
//	for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%lf ",g[i][j]);puts("");}puts("");
	f[n]=g[n][n+1]/g[n][n];
	for(int i=n-1;i>=1;i--)f[i]=(g[i][n+1]-g[i][i+1]*f[i+1])/g[i][i];
}
int main(){
	scanf("%d%d%d%d",&m,&n,&X,&Y),m-=X-1,X=1;
	if(m==1){puts("0");return 0;}
	if(n==1){printf("%d\n",(m-1)*2);return 0;}
	for(int i=1;i<m;i++){
		g[1][1]=2,g[1][2]=-1,g[1][n+1]=f[1]+3;
		g[n][n]=2,g[n][n-1]=-1,g[n][n+1]=f[n]+3;
		for(int j=2;j<n;j++)g[j][j-1]=g[j][j+1]=-1,g[j][j]=3,g[j][n+1]=f[j]+4;
		Giaos();
	}
	printf("%lf\n",f[Y]);
	return 0;
}
  1. 因为“保留4位小数”,所以……

50遍最普通的DP完事。

代码:

#include<bits/stdc++.h> 
using namespace std;
int n,m,X,Y;
double f[1010][1010];
int main(){
	scanf("%d%d%d%d",&m,&n,&X,&Y),m-=X-1,X=1;
	if(m==1){puts("0");return 0;}
	if(n==1){printf("%d\n",(m-1)*2);return 0;}
	for(int i=1;i<m;i++)for(int tmp=1;tmp<=50;tmp++)for(int j=1;j<=n;j++){
		if(j==1)f[i][j]=(f[i][j+1]+f[i][j]+f[i-1][j])/3+1;
		else if(j==n)f[i][j]=(f[i][j-1]+f[i][j]+f[i-1][j])/3+1;
		else f[i][j]=(f[i-1][j]+f[i][j]+f[i][j-1]+f[i][j+1])/4+1;
	}
	printf("%lf\n",f[m-1][Y]);
	return 0;
}

posted @   Troverld  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示