• 记忆化搜索(学习笔记)


    不记得在哪里看到过,记忆化搜索的本质其实就是DP,但看在它叫做搜索,就暂且把它搁在这儿吧~~

    记忆化搜索,顾名思义,就是在搜索的同时,记忆每一次搜索到的状态的结果,下次再碰到这个状态,就可以直接得到结果了.

    可以看出记忆化搜索保证了每一个状态只搜索一次,从而大大地提高运行效率.

    我们常把搜索到的结果(即要记忆的东西)放入一个数组中.

    [SHOI2002] 滑雪

    int n,m,ans;
    int high[105][105],f[105][105];
    int dx[4]={0,0,1,-1},
        dy[4]={1,-1,0,0};
    int dfs(int x,int y){
        if(f[x][y])return f[x][y];
    //之前搜过这个状态,就可以直接调用已经记忆好的结果
    //这里体现了记忆化的好处
        int cnt=1;
    //不论从哪个点开始出发,长度初始都为1(即出发点本身)
        for(int i=0;i<=3;i++){
    		int xx=x+dx[i],yy=y+dy[i];
    		if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&high[xx][yy]<high[x][y])
    //high[xx][yy]与high[x][y]的关系大于或小于都可以
    //大于,相当于以[x,y]为终点.逆序向起点搜索
    //小于,相当于以[x,y]为起点,顺序向终点搜索
    	    	cnt=max(dfs(xx,yy)+1,cnt);
    //这里+1一定不能漏
    //上面cnt=1算的是起点,这里+1加的是终点
        }
        return f[x][y]=cnt;
    //返回此次搜索结果的同时,把结果记忆化到数组中
    //这里体现的就是记忆化
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++){
    	    high[i][j]=read();
    	}
        for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++){
    	    ans=max(ans,dfs(i,j));
    	}
        printf("%d
    ",ans);
        return 0;
    }
    
    

    [HNOI2013] 比赛

    此次联赛共N支球队参加,比赛规则如下:

    (1)每两支球队之间踢一场比赛。

    (2)若平局,两支球队各得1分。

    (3)否则胜利的球队得3分,败者不得分。

    已知每只球队的最后总得分,求有多少种可能的比赛过程?由于答案可能很大,你只需要输出答案对(10^9+7)取模的结果

    剪枝1:

    3(*)输赢局+2(*)平局=所有队伍的总分

    输赢局+平局=(n*(n-1)/2)

    通过这两个方程可表示出输赢局和平局

    剪枝2:如果当前该队伍的分数已经大于它应有的分数,直接返回(因为比赛中没有扣分局)

    剪枝3:如果当前该队伍在接下来的所有场比赛中都获胜,但达不到它应有的分数,直接返回

    剪枝4:对于一个得分序列,可以发现不论它如何排列,最终答案都不会变,因此可以将得分序列从大到小排列,来缩小状态数

    剪枝5:搜索第一个队伍和其它队伍的比赛结果,就可以得到剩下n-1个队伍的得分序列,以此递归搜索,同时用每个队伍的得分序列和队伍个数作为状态hash起来进行记忆化

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int n,tot,wj,pj;
    int a[15],now[15],b[15];
    //a[i]球队i最后得分,now[i]球队i当前得分
    map<int,int>hash;
    bool cmp(int x,int y){
        return x>y;
    }
    int dfs(int x,int y){
        int ans=0;
        if(x==n)return 1;//所有球队都搜索完了
        if(now[x]>a[x])return 0;//剪枝2
        if(now[x]+3*(n-y+1)<a[x])return 0;//剪枝3
        if(y>n){//球队x与其它所有球队的比赛情况搜完了
    		for(int i=x+1;i<=n;i++)
        		b[i]=a[i]-now[i];
    //这时我们得到了剩下队伍(x+1...n)的新的得分序列
    		sort(b+x+1,b+n+1,cmp);
    //对这个新的序列按照得分从大到小排序,跟之前一样了
    		int sum=0;
    		for(int i=x+1;i<=n;i++)
        		sum=sum*28+b[i];
    //把x+1...n队伍的得分情况哈希起来
    //n<=10,所以每支队伍最多有27分,所以哈希乘28就可以了
    		if(hash.find(sum)!=hash.end())
        		return hash[sum];
    		else return hash[sum]=dfs(x+1,x+2);
    //哈希之后的记忆化搜索
        }
    //三个if语句讨论三种情况,记得回溯
        if(now[x]+3<=a[x]&&wj>=1){
    		wj--;now[x]+=3;
    		ans+=dfs(x,y+1);
    		wj++;now[x]-=3;
        }
        if(now[x]+1<=a[x]&&now[y]+1<=a[y]&&pj>=1){
    		pj--;now[x]++;now[y]++;
    		ans+=dfs(x,y+1);
    		pj++;now[x]--;now[y]--;
        }
        if(now[y]+3<=a[y]&&wj>=1){
    		wj--;now[y]+=3;
    		ans+=dfs(x,y+1);
    		wj++;now[y]-=3;
        }
        return ans%mod;
    }
    int main(){
        n=read();//n只球队
        for(int i=1;i<=n;i++){
    		a[i]=read();
    		tot+=a[i];
        }
    //读入每支球队最后的总得分,并计算所有球队的总得分之和
        sort(a+1,a+n+1,cmp);
    //按照得分从大到小排序,对应剪枝4
        wj=tot-n*(n-1);
        pj=n*(n-1)/2-wj;
    //wj输赢局数,pj平局数
    //对应剪枝1
        printf("%d
    ",dfs(1,2)%mod);
    //根据剪枝5
    //我们先要搜索球队1和其它所有球队的比赛情况
    //dfs(1,2)中1表示球队1,2表示球队2
        return 0;
    }
    
    

    2019.1.26:上午做了几道可以用记忆化搜索做的题,貌似别人都用DP做的...我主要是想练习记忆化搜索...

    接苹果

    蹄子,剪刀,布

    花店橱窗布置

    机器分配(这道直接爆搜,因为题目要求输出字典序最小的方案,而深搜的话,第一次找到的合法的方案一定就是字典序最小的方案.)

  • 相关阅读:
    正则化--Lambda
    uwsgi配置cheaper模式进行自动弹性
    采集容器内存并写到excel
    通过进程id找到进程对应的容器并统计每个进程的内存占用写到excel里
    基于celery的任务管理
    基于Redis做内存管理
    uWSGI
    nginx
    服务发现
    绑核与巨页
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10319934.html
Copyright © 2020-2023  润新知