不记得在哪里看到过,记忆化搜索的本质其实就是DP,但看在它叫做搜索,就暂且把它搁在这儿吧~~
记忆化搜索,顾名思义,就是在搜索的同时,记忆每一次搜索到的状态的结果,下次再碰到这个状态,就可以直接得到结果了.
可以看出记忆化搜索保证了每一个状态只搜索一次,从而大大地提高运行效率.
我们常把搜索到的结果(即要记忆的东西)放入一个数组中.
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;
}
此次联赛共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做的...我主要是想练习记忆化搜索...
机器分配(这道直接爆搜,因为题目要求输出字典序最小的方案,而深搜的话,第一次找到的合法的方案一定就是字典序最小的方案.)