zoukankan      html  css  js  c++  java
  • CF24D Broken robot

    IL.CF24D Broken robot

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

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

    还能怎么办?耍回去啊!

    例如这题,有两种思路。

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

    我们看一下怎么高斯消元。设有\(m\)\(n\)列。

    则有

    \(f_{i,j}=\begin{cases}[j=1]:\dfrac{f_{i,j}+f_{i-1,j}+f_{i,j+1}}{3}+1\\ [j=n]:\dfrac{f_{i,j}+f_{i-1,j}+f_{i,j-1}}{3}+1\\\text{otherwise}:\dfrac{f_{i,j}+f_{i-1,j}+f_{i,j-1}+f_{i,j+1}}{4}+1\end{cases}\)

    处理一下:

    \(\begin{cases}[j=1]:2f_{i,j}-f_{i,j+1}=f_{i-1,j}+3\\ [j=n]:2f_{i,j}-f_{i,j-1}=f_{i-1,j}+3\\\text{otherwise}:3f_{i,j}-f_{i,j-1}-f_{i,j+1}=f_{i-1,j}+4\end{cases}\)

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

    这样复杂度是\(O(n^4)\),铁定过不去。

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

    \(\begin{bmatrix}2&-1&0&0&0\\-1&3&-1&0&0\\0&-1&3&-1&0\\0&0&-1&3&-1\\0&0&0&-1&2\end{bmatrix}\)

    更大一点:

    \(\begin{bmatrix}2&-1&0&\cdots&0&0&0\\-1&3&-1&\cdots&0&0&0\\0&-1&3&\cdots&0&0&0\\\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\0&0&0&\cdots&3&-1&0\\0&0&0&\cdots&-1&3&-1\\0&0&0&\cdots&0&-1&2\end{bmatrix}\)

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

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

    则总复杂度是\(O(n^2)\)的。

    另外,从点\((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;
    }
    

  • 相关阅读:
    操作符重载
    虚继承
    虚函数(2)
    基类与子类的成员函数的关系
    虚函数
    虚函数的简单应用
    齐国的粮食战
    纯虚函数
    类的继承(2)
    输出自定义日期格式
  • 原文地址:https://www.cnblogs.com/Troverld/p/14597380.html
Copyright © 2011-2022 走看看