题目描述:
思路
对抗搜索
一、适用范围
在博弈论题目中,如果决策双方的获胜条件是截然相反的(即一方要求得分越高越好,另一方要求得分越低越好),这时我们就可以用上对抗搜索算法。
二、主要思路
dfs遍历博弈树。但是如果博弈树非常庞大,在不加优化的情况下,对抗搜索的时间效率十分低下,所以对抗搜索进行一定的优化。
三、主要优化
记忆化 和 Alpha−Beta剪枝 。
这里用的是记忆化(因为记忆化简单/(ㄒoㄒ)/~~)
再看这个题,经典对抗搜索,怎么记忆化呢??( 看看题目,n,m<=500 , s<=50 , 这不记忆化就来了。。。)
f [ x ] [ y ] [ s ] 记录当前位于 x , y , 且钱数是 s 的状态(记录当前的状态),下一次再访问到时直接将记录下来的结果返回即可。
同时自己的回合跟表弟的回合都可能遍历两种或者四种状态:
如果此点不是商店,那么就是 当前钱数+向下 、当前钱数+向右 两种状态;
如果此点是商店,那么就是 当前钱数+向下,当前钱数+向右 或者 0 + 向下、0 + 向右 四种状态( 当前钱数变为0,是因为换取水晶钱都没了。。。)
剩下的就是常规搜了,没啥坑点了(哦,还有,会爆int , 开long long)
代码
#include<iostream>
#include<string>
#include<map>
#include<set>
//#include<unordered_map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<fstream>
#define X first
#define Y second
#define INF 0x3f3f3f3f
#define pii pair<int, int>
//#define pdi pair<double,int>
//#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
const int maxn=1e6+10;
int n,m,w,a[510][510];
ll f[510][510][60];
bool vis[510][510];
struct node
{
int s;
int d;
}p[510][510];
bool check(int x,int y)
{
if(x>n||y>m) return 0;
else return 1;
}
ll dfs(int x,int y,int pos,int money)
{
money=min(50,money);
//因为 s 最多是 50 ,所以如果当前你所拥有的钱 >= 50 ,事实上就相当于你只拥有 50。
if(f[x][y][money]!=-1) return f[x][y][money];
//记忆化,避免超时
if(x==n&&y==m) return 0;
//如果到终点了,就返回 0 (因为终点不可能有商店,所以获得的水晶数目是 0 )
ll ans,flag=0;
if(pos)//表弟
{
ans=1ll*INF*INF;
if(vis[x][y])
if(money>=p[x][y].s)
flag=1; //标记兑换了水晶 ,金钱要变为 0
if(check(x+1,y)&&flag) ans=min(ans,p[x][y].d+dfs(x+1,y,pos^1,0+a[x+1][y]));
if(check(x,y+1)&&flag) ans=min(ans,p[x][y].d+dfs(x,y+1,pos^1,0+a[x][y+1]));
if(check(x+1,y)) ans=min(ans,dfs(x+1,y,pos^1,money+a[x+1][y]));
if(check(x,y+1)) ans=min(ans,dfs(x,y+1,pos^1,money+a[x][y+1]));
}
else//我
{
ans=-1ll*INF*INF;
if(vis[x][y])
if(money>=p[x][y].s)
flag=1; //标记兑换了水晶 ,金钱要变为 0
if(check(x+1,y)&&flag) ans=max(ans,p[x][y].d+dfs(x+1,y,pos^1,0+a[x+1][y]));
if(check(x,y+1)&&flag) ans=max(ans,p[x][y].d+dfs(x,y+1,pos^1,0+a[x][y+1]));
if(check(x+1,y)) ans=max(ans,dfs(x+1,y,pos^1,money+a[x+1][y]));
if(check(x,y+1)) ans=max(ans,dfs(x,y+1,pos^1,money+a[x][y+1]));
}
return f[x][y][money]=ans;
}
int main( )
{
// ios::sync_with_stdio(false);
memset(f,-1,sizeof(f));
//不赋值为 0 的原因是,如果一个商店也没有的话,那么记忆化的 f 数组也是0,跟没搜之前一样,那就成爆搜了,会 T
//但事实上你可能已经搜过了,所以赋值为 -1 ,就可以避免这样的事情发生。
scanf("%d%d%d",&n,&m,&w);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=w;i++)
{
int x,y,s,d;
scanf("%d%d%d%d",&x,&y,&s,&d);
p[x][y].s=s;
p[x][y].d=d;
vis[x][y]=1;
}
printf("%lld",dfs(1,1,0,a[1][1]));
return 0;
}