1.题目
考虑如下的小游戏:玩家凭借一张地图,利用初始资金购买一定数量的水和食物(包括食品和其他日常用品),从起点出发,在沙漠中行走。途中会遇到不同的天气,也可在矿山、村庄补充资金或资源,目标是在规定时间内到达终点,并保留尽可能多的资金。
游戏的基本规则如下:
(1)以天为基本时间单位,游戏的开始时间为第0天,玩家位于起点。玩家必须在截止日期或之前到达终点,到达终点后该玩家的游戏结束。
(2)穿越沙漠需水和食物两种资源,它们的最小计量单位均为箱。每天玩家拥有的水和食物质量之和不能超过负重上限。若未到达终点而水或食物已耗尽,视为游戏失败。
(3)每天的天气为“晴朗”、“高温”、“沙暴”三种状况之一,沙漠中所有区域的天气相同。
(4)每天玩家可从地图中的某个区域到达与之相邻的另一个区域,也可在原地停留。沙暴日必须在原地停留。
(5)玩家在原地停留一天消耗的资源数量称为基础消耗量,行走一天消耗的资源数量为基础消耗量的倍。
(6)玩家第0天可在起点处用初始资金以基准价格购买水和食物。玩家可在起点停留或回到起点,但不能多次在起点购买资源。玩家到达终点后可退回剩余的水和食物,每箱退回价格为基准价格的一半。
(7)玩家在矿山停留时,可通过挖矿获得资金,挖矿一天获得的资金量称为基础收益。如果挖矿,消耗的资源数量为基础消耗量的倍;如果不挖矿,消耗的资源数量为基础消耗量。到达矿山当天不能挖矿。沙暴日也可挖矿。
(8)玩家经过或在村庄停留时可用剩余的初始资金或挖矿获得的资金随时购买水和食物,每箱价格为基准价格的2倍。
请根据游戏的不同设定,建立数学模型,解决以下问题。
- 假设只有一名玩家,在整个游戏时段内每天天气状况事先全部已知,试给出一般情况下玩家的最优策略。求解附件中的“第一关”和“第二关”,并将相应结果分别填入Result.xlsx。
- 假设只有一名玩家,玩家仅知道当天的天气状况,可据此决定当天的行动方案,试给出一般情况下玩家的最佳策略,并对附件中的“第三关”和“第四关”进行具体讨论。
2.解题思路与第一关与第二关
有三大种思路
1.动态规划
2.简略地图后使用Dijkstra算法来解决
3.分析题干然后建立模型利用groubi,lingo求解最优解
2.1 动态规划情况
设状态dp[k][j][w][f]代表第k天时在第j个点剩余水为w箱剩余食物为f箱的最大资金,则:
ans=MAXw,f,i dp[k][zd][w][f]
2.1.1 初始值设定
由于起始点可以在起点购买物资,则有初始状态
dp[0][qd][w][f]=10000−cost_water∗w−cost_food∗fw∈[0,400]f∈[0,600]
其中cost_water,cost_food 为购买水和食物消耗的钱。
这里假设起始为第0天,则第一天天气影响的是从第0天到第1天。
2.1.2 状态转移方程
当第k天人在村庄j时:
- 第k天为沙暴天气
dp[k+1][j][w+ww-xh_water[tq]][f+ff-xh_food[tq]]}=max(dp[k][j][w][f]-2*ww*cost_water-2*ff*cost_food
- 非沙暴天气:
dp[k+1][jj][w+ww-xh_water[tq]][f+ff-xh_food[tq]]}=max(dp[k][j][w][f]-2*ww*cost_water-2*ff*cost_food
当第k天人在矿山j时:
挖矿
dp[k+1][j][w-dig*xh_water[tq]][f-dig*xh_food[tq]]=max(dp[k][j][w][f]+1000)
第k天为沙暴天气:
dp[k+1][j][w-xh_water[tq]][f-xh_food[tq]]=max(dp[k][j][w][f])
第k天为非沙暴天气:
dp[k+1][jj][w-walk*xh_water[tq]][f-walk*xh_food[tq]]=max(dp[k][jj][w][f])
当第k天人在其他地区时
第k天为沙暴天气:
dp[k+1][j][w-xh_water[tq]][f-xh_food[tq]]=max(dp[k][j][w][f])
第k天为非沙暴天气:
dp[k][jj][w-walk*xh_water[tq]][f-walk*xh_food[tq]]=max(dp[k][j][w][f])
2.1.3思考隐含条件约束模型
除了回村庄补充物资,不会走回头路。
除了挖矿和沙尘暴不会在原地停留。
只会走关键点之间的最短路径
2.1.4代码
第一关
#include
#include
using namespace std;
// 点数
const int N=11,M=28,inf=0x3f3f3f,Day=30;
int dp[32][N+1][405][605],zd,qd,FZ;
int cost_water,cost_food,walk,dig,buy;
int xh_water[3]={5,8,10},xh_food[3]={7,6,10};
bool cz[N+1],ks[N+1];
struct node
{
short day; // i
short from; // jj j
int water,food;
int money;
bool operator!=(const node &x){
return x.day!=day || x.from!=from || x.water!=water || x.food!=food ;
};
}path[31][N+1][405][605],lastpath;
vector <int> weather;
vector <int> g[N];
map <int,int> mp;
void push_back(int x,int y)
{
g[x].push_back(y);
g[y].push_back(x);
}
void build_map()
{
push_back(1,2);
push_back(2,3);
push_back(2,5);
push_back(5,6);
push_back(3,4);
push_back(4,7);
push_back(6,7);
push_back(7,8);
push_back(8,9);
push_back(9,10);
push_back(10,11);
mp[1]=1;
mp[2]=25;
mp[3]=26;
mp[4]=27;
mp[5]=24;
mp[6]=23;
mp[7]=21;
mp[8]=9;
mp[9]=15;
mp[10]=14;
mp[11]=12;
for(int i=1;i<=N;i++)
{
cz[i]=0;
ks[i]=0;
}
cz[9]=1;
ks[11]=1;
zd=4;
qd=1;
return ;
}
void init()
{
memset(dp,-inf,sizeof(dp));
FZ=1200;
cost_water=5;
cost_food=10;
walk=2;
buy=2;
dig=3;
for(int k=0;k<=405;k++)
{
for(int l=0;l<=601;l++)
{
if(k*3+l*2<=FZ)
{
dp[0][qd][k][l]=10000-k*cost_water-l*cost_food;
}
}
}
printf("init %dn",dp[0][1][178][333]);
path[0][1][0][0]={0,0,0,0};
return ;
}
int main()
{
weather={
1,1,0,2,0,1,2,0,1,1,
2,1,0,1,1,1,2,2,1,1,
0,0,1,0,2,1,0,0,1,1,
};
build_map();
init();
for(int i=0;i<Day;i++)
{
printf("第%d天n",i);
int tq=weather[i];
for(int j=1;j<=N;j++)
{
if(cz[j])// 村庄
{
for(int w=0;w<=405;w++)
{
for(int f=0;w*3+f*2<=1200;f++)
{
//购买或不够买物资(ww=0,ff=0就是不购买)
if(tq==2) //停留
{
int money=dp[i][j][w][f];
for(int ww=0;ww<=money/cost_water;ww++)
{
for(int ff=0;ff<=(FZ-(w+ww)*3)/2-f;ff++)
{
if(w+ww-xh_water[tq]>=0&&f+ff-xh_food[tq]>=0&&dp[i][j][w][f]-2*ww*cost_water-2*ff*cost_food>=0)
{
if(dp[i+1][j][w+ww-xh_water[tq]][f+ff-xh_food[tq]]<dp[i][j][w][f]-2*ww*cost_water-2*ff*cost_food)
{
dp[i+1][j][w+ww-xh_water[tq]][f+ff-xh_food[tq]]=dp[i][j][w][f]-2*ww*cost_water-2*ff*cost_food;
path[i+1][j][w+ww-xh_water[tq]][f+ff-xh_food[tq]]={i,j,w,f,dp[i][j][w][f]-2*ww*cost_water-2*ff*cost_food};
}
}
}
}
}
else //从j走到jj
{
for(auto jj:g[j])
{
int money=dp[i][j][w][f];
for(int ww=0;ww<=money/cost_water;ww++)
{
for(int ff=0;ff<=(FZ-(w+ww)*3)/2-f;ff++)
{
if(w+ww-walk*xh_water[tq]>=0&&f+ff-walk*xh_food[tq]>=0&&dp[i][j][w][f]-buy*ww*cost_water-buy*ff*cost_food>=0)
{
if(dp[i+1][jj][w+ww-walk*xh_water[tq]][f+ff-walk*xh_food[tq]]<dp[i][j][w][f]-buy*ww*cost_water-buy*ff*cost_food)
{
dp[i+1][jj][w+ww-walk*xh_water[tq]][f+ff-walk*xh_food[tq]]=dp[i][j][w][f]-buy*ww*cost_water-buy*ff*cost_food;
path[i+1][jj][w+ww-walk*xh_water[tq]][f+ff-walk*xh_food[tq]]={i,j,w,f,dp[i][j][w][f]-buy*ww*cost_water-buy*ff*cost_food};
}
}
}
}
}
}
}
}
}
else if (ks[j])// 矿山
{
for(int w=0;w<=405;w++)
{
for(int f=0;w*3+f*2<=1200;f++)
{
// 已经停留一天了,可以挖矿
if(w-dig*xh_water[tq]>=0&&f-dig*xh_food[tq]>=0)
{
if(dp[i+1][j][w-dig*xh_water[tq]][f-dig*xh_food[tq]]<dp[i][j][w][f]+1000&&dp[i][j][w][f]>=0)
{
dp[i+1][j][w-dig*xh_water[tq]][f-dig*xh_food[tq]]=dp[i][j][w][f]+1000;
path[i+1][j][w-dig*xh_water[tq]][f-dig*xh_food[tq]]={i,j,w,f,dp[i][j][w][f]+1000};
}
}
// 在矿山不挖矿或 不允许挖矿
if(tq==2) //停留但不挖矿
{
if(w-xh_water[tq]>=0&&f-xh_food[tq]>=0)
{
if(dp[i+1][j][w-xh_water[tq]][f-xh_food[tq]]<dp[i][j][w][f]&&dp[i][j][w][f]>=0)
{
dp[i+1][j][w-xh_water[tq]][f-xh_food[tq]]=dp[i][j][w][f];
path[i+1][j][w-xh_water[tq]][f-xh_food[tq]]={i,j,w,f,dp[i][j][w][f]};
}
}
}
else
{
if(w-walk*xh_water[tq]>=0&&f-walk*xh_food[tq]>=0)
{
for(auto jj:g[j])
{
if(dp[i+1][jj][w-walk*xh_water[tq]][f-walk*xh_food[tq]]<dp[i][j][w][f]&&dp[i][j