冒险者



题解

本蒟蒻采用的和大部分人解法不同,是根据当前标记值的总和跑最短路的一种解法。

思路 30min ,调代码 2h 的我太蒻了

首先观察题面可以发现本题求的是最少操作数,由于要求最小且有变化的过程,所以可以使用 dp 求解,也可以使用 最短路算法 求解,本篇先介绍最短路的算法。

其实作为图论来解还挺需要思维的。

最短路做法

建图

可以把本题抽象为以下的问题:

以编号为 \(\sum_{i = 1}^{n} a_i\) 的节点为起点,每次可以减去任何一个 \(a_i\) 并到达编号为这个数的节点,每个 \(a_i\) 最多可以减去一次;每次也可以加上一个 \(1\) 并到达编号为这个数的节点,有无限次数,任何边的权值都为 \(1\) ,求到达编号为 \(x\) 的倍数的节点的最短路。

实现

写题解的时候突然发现由于边权为 1 ,所以本题可以使用 bfs 用 \(O(n)\) 的时间进行求解,数据甚至可以更大一些。

但考试的时候没想那么多,就使用了 Dijkstra 算法,时间为 \(O(m \log m)\),由于边数可能比较多,因此不提前建出边来,而是对每个节点临时建边。看似复杂度很大,实际上有效边数并没有这么多,最大的数据也只跑了 13ms ,若用 bfs 可能会更少。

同时注意到每个数只能被减去一次,由于堆优化 dijkstra 的堆中每个情况是相对独立的,所以每个情况都要开额外的一个数组来存储每个数是否使用过,为了节省空间防止 MLE ,这里采用 bitset 来压缩空间。就是这里写错下标导致我调了 2h。

然后其他的就按照正常的最短路跑就行了。

代码

直接贺了赛时的 dijkstra 上去,什么时候有时间再来写 bfs 版和 dp 版的吧。

#include <bits/stdc++.h>
using namespace std;
int n,x;
int a[1005],start=0;
int dis[1001005];
bool vis[1001005];
struct node{
bitset<1005>bs;
int f,s;
}tmp,tmp2;
struct cmp{
bool operator()(node b,node c)
{
return c.f<b.f;
}
};
int dijkstra()
{
memset(dis,0x3f,sizeof(dis));
priority_queue<node,vector<node>,cmp> q;
tmp.f=0,tmp.s=start;
q.push(tmp);
dis[start]=0;
while(!q.empty())
{
tmp=q.top();
q.pop();
int oridis=tmp.f,u=tmp.s;
if(u%x==0)
{
return dis[u];
}
if(vis[u])continue;
vis[u]=1;
for(int i=1;i<=n;i++)
{
if(tmp.bs[i]==0)
{
int v=u-a[i];
if(dis[v]>oridis+1)
{
dis[v]=oridis+1;
tmp2.f=dis[v],tmp2.s=v;
tmp2.bs=tmp.bs;
tmp2.bs[i]=1;//就是这个下标坑我2h
q.push(tmp2);
}
}
}
int v=u+1;
if(v<=start+1000 && dis[v]>oridis+1)//最多操作次数为1000次
{
dis[v]=oridis+1;
tmp2.f=dis[v],tmp2.s=v;
tmp2.bs=tmp.bs;
q.push(tmp2);
}
}
return n;
}
int main()
{
freopen("player.in","r",stdin);
freopen("player.out","w",stdout);
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];
start+=a[i];
}
int res=dijkstra();
cout<<res;
return 0;
}

dp 做法

没有想到这个做法的原因主要还是最近 dp 有点生疏,一直往利用元素的条件来想,而忽视了靠元素的数值来解决问题。

这是典型的以数字设计 dp ,而不是用元素设计的一道题,从 \(a_i\le 1000\) 就可以看出。

同时,对于一个数删不删除,对应着 2 种状态,并且一旦知道删了哪些数,就可以推出这时要加多少个 1 。所以,就此设计转移方程式即可。

第一维是一个常见的 dp 设计思路:对于前 \(i\) 个数进行选择的结果,第二维是当前的数的总和 $sum \mod x $ 的结果。

于是,定义 \(f[i][j]\) 表示在前 \(i\) 个数中,使存在的数的总和 \(\mod x\) 为 \(j\) 时,最少删去的数的数量。

转移方程式为:\(f[i][j]=\min(f[i-1][j]+1,f[i-1][(j-a[i]+x)\mod x])\) ,其中 $ 1\le i\le n , 0\le j <x $。

最后计算答案时,直接输出 $\min(f[n][j]+(x-j)\mod x) , 0\le j <x $ 即可。

然后还要注意一点:初始化 \(f[0][0]=0\) 。

时间 \(O(nx)\) 。

未优化代码如下:

#include <bits/stdc++.h>
using namespace std;
int a[1005],n,f[1005][1005],x;
int main()
{
freopen("player.in","r",stdin);
freopen("player.out","w",stdout);
memset(f,0x3f,sizeof(f));
f[0][0]=0;
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]%=x;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<x;j++)
{
f[i][j]=min(f[i-1][j]+1,f[i-1][(j-a[i]+x)%x]);
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<x;i++)
{
ans=min(ans,f[n][i]+(x-i)%x);
}
cout<<ans;
return 0;
}

优化

注意到动态转移方程式的第一维只会用到 \(i-1\) 的值,所以可以强行进行滚动数组优化,虽然不这样做也可以过就是了。

滚动数组优化代码:

#include <bits/stdc++.h>
using namespace std;
int a[1005],n,f[2][1005],x;
int main()
{
freopen("player.in","r",stdin);
freopen("player.out","w",stdout);
memset(f,0x3f,sizeof(f));
f[0][0]=0;
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]%=x;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<x;j++)
{
f[i&1][j]=min(f[(i&1)^1][j]+1,f[(i&1)^1][(j-a[i]+x)%x]);//i&1=i%2,(i&1)^1=(i+1)%2
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<x;i++)
{
ans=min(ans,f[n&1][i]+(x-i)%x);
}
cout<<ans;
return 0;
}

Hetao P1178 冒险者 题解 [ 绿 ][ 最短路 ][ 线性 dp ]的更多相关文章

  1. [luogu1772 ZJOI2006] 物流运输 (最短路 线性dp)

    题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪. ...

  2. 【NOI2005】聪聪与可可 题解(最短路+期望DP)

    前言:学长讲的太神了:自己还能推出来DP式子,挺开心. -------------------------- 题目链接 题目大意:给定一张含有$n$个结点$m$条边的无向连通图.现在聪聪在点$s$,可 ...

  3. 【题解】LOJ6060 Set(线性基)

    [题解]LOJ6060 Set(线性基) orz gql 设所有数的异或和为\(S\),答案是在\(\max (x_1+S\and x_1)\)的前提下\(\min x_1\)输出\(x_1\) 转换 ...

  4. Codeforces 176B (线性DP+字符串)

    题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28214 题目大意:源串有如下变形:每次将串切为两半,位置颠倒形成 ...

  5. POJ 2479-Maximum sum(线性dp)

    Maximum sum Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 33918   Accepted: 10504 Des ...

  6. 【洛谷P1854】花店橱窗 线性dp+路径输出

    题目大意:给定 N 个数字,编号分别从 1 - N,M 个位置,N 个数字按照相对大小顺序放在 M 个位置里,每个数放在每个位置上有一个对答案的贡献值,求一种摆放方式使得贡献值最大. 题解:一道典型的 ...

  7. 线性dp

    线性dp应该是dp中比较简单的一类,不过也有难的.(矩乘优化递推请出门右转) 线性dp一般是用前面的状态去推后面的,也有用后面往前面推的,这时候把循环顺序倒一倒就行了.如果有的题又要从前往后推又要从后 ...

  8. [CodeForces - 1272D] Remove One Element 【线性dp】

    [CodeForces - 1272D] Remove One Element [线性dp] 标签:题解 codeforces题解 dp 线性dp 题目描述 Time limit 2000 ms Me ...

  9. 【题解】NOIP2017逛公园(DP)

    [题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n​节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...

  10. 非常完整的线性DP及记忆化搜索讲义

    基础概念 我们之前的课程当中接触了最基础的动态规划. 动态规划最重要的就是找到一个状态和状态转移方程. 除此之外,动态规划问题分析中还有一些重要性质,如:重叠子问题.最优子结构.无后效性等. 最优子结 ...

随机推荐

  1. 分布式对象存储之FDFS

    1.它是一个开源的分布式文件系统,它对文件进行管理. 功能有:文件存储.文件同步.文件访问(文件的上传下载)等.特别适合以文件为主的在线服务. 2.fastDFS服务端有两个角色:跟踪器(tracke ...

  2. docker构建supervisor镜像

    1 介绍 记录使用docker 构建包含 supervior 的镜像, supervisor: 是一个管理和监控进程的程序,可以方便的通过配置文件来管理我们的任务脚本 将supervisor构建到系统 ...

  3. 再也不用写请求HttpHelper了,HttpClient帮助你

    前言 在C#7.1之后,net推出HttpClient类代替WebRequest, HttpWebRequest, ServicePoint, and WebClient ,先来看下他们在以前的作用 ...

  4. 渗透测试-前端验签绕过之SHA256

    本文是高级前端加解密与验签实战的第1篇文章,本系列文章实验靶场为Yakit里自带的Vulinbox靶场,本文讲述的是绕过SHA256签名来爆破登录. 绕过 通过查看源代码可以看到key为 123412 ...

  5. 双语对照的 PDF 翻译工具「GitHub 热点速览」

    在 OpenAI 举办的「12天12场」发布会上,ChatGPT 的多项新功能正式亮相,包括 GPT-o1 正式版和 ChatGPT Pro(200 美元/月).强化微调(Reinforcement ...

  6. 钉钉机器人发送信息shell

    #钉钉机器人发送信息shell 可作为shell函数模块调用,用于监控警报.jenkins发版通知等 微信API官方文档 https://ding-doc.dingtalk.com/doc#/serv ...

  7. 【Javaweb】【Maven】【Tomcat10】jsp访问正常,Servlet访问404

    Development Tool:IDEA(Intellij IDEA) Tomcat-Version:10 JDK-version:17 情况描述 我使用Tomcat10运行项目,jsp正常访问,但 ...

  8. IDE提交Git出现husky>pre-commit错误

    若使用IDE提交Git出现以下错误: 则是ES6在提交校验过程中出现了问题,强制提交方式为: 命令行: git commit --no-verify IDEA: 在提交时取消勾选Run Git hoo ...

  9. 微服务之调用链(Feign+SpringCloud)

    终于到了我们的重点,微服务了. 与使用OkHttp3来实现的客户端类似,Feign接口本来也就是一个Http调用,依然可以使用Http头传值的方式,将 Trace 往下传. 本文更多的是关于 Spri ...

  10. Qt/C++监控推流设备推流/延迟极低/实时性极高/rtsp/rtmp推流/hls/flv/webrtc拉流/调整分辨率降低带宽

    一.前言 算下来这个推流的项目作品写了有四年多了,最初第一个版本只有文件点播的功能,用的纯QTcpSocket通信实现,属于比较简单的功能.由于文件点播只支持文件形式的推流,不支持网络流或者本地设备采 ...