Luogu-P2295 MICE
题目
测试得分: 100
主要算法 : 记忆化搜索,动态规划
题干:
有后效性DP?
分析
伪记忆化搜索:
考虑起点的特殊情况,预先处理
Dfs搜出答案,每一次路径看到的老鼠,加上老鼠数目,把看到的老鼠数目所在点的老鼠删除掉
记得回溯最终得分40分
代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc getchar()//pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),stdin)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; char buf[100000],*pa,*pb; inline int read(); const int N=1100,M=1100,INF=2147483647; int n,m,a[N+1][M+1],s[N+1][M+1],f[N+1][M+1][2]; int ans=INF,mx[]={0,1,-1,0},my[]={1,0,0,-1}; inline int min(int fa,int fb){return fa<fb?fa:fb;} void Init() { n=read(),m=read(); FORa(i,1,n) FORa(j,1,m) a[i][j]=read(); } void Dfs(int x,int y,int cnt) { if(s[x][y]<cnt||cnt>=ans) return; s[x][y]=cnt; if(x==n&&y==m) { ans=min(cnt,ans); return; } int fx,fy,p[4]; FORa(i,0,1) { fx=x+mx[i],fy=y+my[i]; if(fx<=n&&fy<=m) { FORa(k,0,3) p[k]=a[fx+mx[k]][fy+my[k]],a[fx+mx[k]][fy+my[k]]=0; Dfs(fx,fy,cnt+p[0]+p[1]+p[2]+p[3]); FORa(k,0,3) a[fx+mx[k]][fy+my[k]]=p[k]; } } } void Solve_BL() { memset(s,127,sizeof(s)); int cnt=a[1][1]+a[1][2]+a[2][1]; a[1][2]=a[2][1]=a[1][1]=0; Dfs(1,1,cnt); printf("%d",ans); } int main() { Init(); Solve_BL(); return 0; } inline int read() { register char c(gc);register int f(1),x(0); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; } /* 5 5 86 19 26 18 92 25 67 69 28 83 54 56 7 5 93 63 33 8 66 91 68 74 56 92 90 */
100分DP
定义:
a[i][j]表示格子(i,j)中老鼠的数量。
f[i][j][0]表示当前小象位于格子(i,j)且上一个位置是(i-1,j)所看见的老鼠的最少数量。
f[i][j][1]表示当前小象位于格子(i,j)且上一个位置是(i,j-1)所看见的老鼠的最少数量
我们可以得到转移方程:
f[i][j][0]=min(f[i-1][j][0]+a[i][j-1],f[i-1][j][1])+a[i+1][j]+a[i][j+1]
f[i][j][1]=min(f[i][j-1][0],f[i][j-1][1]+a[i-1][j])+a[i+1][j]+a[i][j+1]
最后答案为 min(f[n][m][0],f[n][m][1])。 复杂度:O(N*M)。
#include<stdio.h> #include<stdlib.h> #include<string.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc getchar()//pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),stdin)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; char buf[100000],*pa,*pb; inline int read(); const int N=1100,M=1100; int n,m,f[N+1][M+1][2],a[N+1][M+1]; inline int min(int fa,int fb){return fa<fb?fa:fb;} void Solve_Dp() { memset(f,63,sizeof(f)); n=read(),m=read(); FORa(i,1,n) FORa(j,1,m) a[i][j]=read(); f[1][1][0]=f[1][1][1]=a[1][1]+a[1][2]+a[2][1]; FORa(i,1,n) FORa(j,1,m) if(i*j!=1) { f[i][j][0]=min(f[i][j-1][0]+a[i-1][j],f[i][j-1][1])+a[i+1][j]+a[i][j+1]; f[i][j][1]=min(f[i-1][j][1]+a[i][j-1],f[i-1][j][0])+a[i+1][j]+a[i][j+1]; } printf("%d\n",min(f[n][m][0],f[n][m][1])); } int main() { File("lemouse"); Solve_Dp(); return 0; } inline int read() { register char c(gc);register int f(1),x(0); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; } /* 5 5 86 19 26 18 92 25 67 69 28 83 54 56 7 5 93 63 33 8 66 91 68 74 56 92 90 */
总结:
确定动规模型