题解:P14508 猜数游戏 guess
看到题,第一反应是建模图论。记
但是这个东西看起来是非常不可做的,于是我们需要另辟蹊径。
考虑到这个棋盘是无穷大且重复的,我们要求
我们定义
我们先从
于是得到转移方程:
计算
发现其实转移时,如果
注意几个实现时的细节:
-
代价极限数据下会超过
int的数据范围,需要开ll。 -
最短路由于在进行松弛操作时可能会出现下标为负的点,因此考虑将所有点右移
A 位后再进行求解,动态规划时候还原。 -
如果暴力建图再进行最短路会导致总边数上界达到
6nmT 导致\text{MLE} ,因此需要在最短路松弛操作内部直接根据m 进行遍历。
AC Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+10;
const int maxm=5e3+10;
typedef long long ll;
const ll INF=1e18;
int n,m,mxk,mnk;
ll dp[maxn],dis[maxm<<2];
struct move
{
int k;
ll v;
}mov[maxm];
struct edge
{
int v;
ll w;
};
struct node
{
int k;
ll dis;
bool operator <(const node &uu)const
{
return dis>uu.dis;
}
};
priority_queue<node>q;
int vis[maxm<<2];
void dijkstra()
{
for(int i=0;i<=2*n+1;i++)
dis[i]=INF,vis[i]=0;
dis[mxk]=0;
q.push({mxk,0});
while(!q.empty())
{
int k=q.top().k;
q.pop();
if(vis[k])
continue;
vis[k]=1;
for(int i=1;i<=m;i++)
{
if(k-mov[i].k<0)
continue;
int to=k-mov[i].k;
ll w=mov[i].v;
if(dis[k]+w<dis[to])
{
dis[to]=dis[k]+w;
q.push({to,dis[to]});
}
}
for(int i=1;i<=m;i++)
{
if(k+mov[i].k>2*n+1)
continue;
int to=k+mov[i].k;
ll w=mov[i].v;
if(dis[k]+w<dis[to])
{
dis[to]=dis[k]+w;
q.push({to,dis[to]});
}
}
}
return;
}
int main()
{
ios::sync_with_stdio(false);
int c,t;
cin>>c>>t;
while(t--)
{
mxk=0,mnk=INF;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>mov[i].k;
mxk=max(mxk,mov[i].k);
mnk=min(mnk,mov[i].k);
}
for(int i=1;i<=m;i++)
cin>>mov[i].v;
dijkstra();
fill(dp,dp+1+n,INF);
dp[1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=min(i-1,mxk);j++)
dp[i]=min(dp[i],max(dp[j],dp[i-j])+dis[j+mxk]);
// for(int j=0;j<=min(n,mxk);j++)
// cout<<j<<"v "<<dis[j+mxk]<<endl;
if(dp[n]==INF)
cout<<-1<<endl;
else
cout<<dp[n]<<endl;
}
return 0;
}