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 */

 

总结:

   确定动规模型

 

posted @ 2019-08-16 20:01  SeanOcean  阅读(171)  评论(0编辑  收藏  举报