冒险者



题解

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

思路 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. golang之go-spew

    github: https://github.com/davecgh/go-spew 我们在使用Golang(Go语言)开发的过程中,会通过经常通过调试的方式查找问题的原因,解决问题,尤其是当遇到一个 ...

  2. Vue3 组合式API

    1.入口 创建实例时,配置setup方法,然后其内部书写组合式API代码,通过组合式API生产的数据和返回,需要暴漏出去才能给HTML使用 <script> //组合式(解构赋值) con ...

  3. Echarts 颜色管理

    1.Echarts的颜色设计 Echarts的颜色的设置分为两种:色盘和具体颜色 色盘适合做全局设置,因为他里面有多个颜色,通俗的说色盘就是颜色预设值列表,色盘统一使用color属性进行配置. 而具体 ...

  4. Vue CLI中views和components文件夹的区别

    首先,src/components和文件夹src/views都包含Vue组件. 关键区别在于某些Vue组件充当路由视图. 在Vue中(通常是Vue Router)处理路由时,将定义路由以切换组件中使用 ...

  5. docker containner挂掉,无法exec进入bash,如何修改文件的终极解决方法

    场景: Nginx在bash里面配置的时候挂掉了,然后docker start不起来,exec bash进不去,造成无法再改里面的文件了 解决方法: 1,docker ps –a 可以查到所有dock ...

  6. 叮咚~ 你的Techo大会云存储专场邀请函到了!

    12月19日至20日,由腾讯主办的2020 Techo Park开发者大会将于北京召开.Techo Park 开发者大会是由腾讯发起的面向全球开发者和技术爱好者的年度盛会,作为一个专注于前沿技术研讨的 ...

  7. 还在为入门鸿蒙效率慢而困惑?不妨试试开发学习神器 - AI辅助编程

    鸿蒙应用开发从入门到入行 鸿蒙开发神器 - AI辅助编程 CodeGenie介绍 目前有好几款AI插件可以装到DevEco上,出名的例如Copilot.通义灵码等.但是经过猫林老师截至到目前的测试.都 ...

  8. 内网穿透之http代理服务器

    在公网访问内网http服务可以用内网穿透工具,例如frp和nps等优秀工具.但我发现这类工具会在服务器启动不止一个端口,对于有些网络审查来说很容易发现在进行内网穿透.因此我想是否可以只在服务器启动一个 ...

  9. 明察秋毫--用ss工具统计网络栈内存使用

    前言 本文介绍了用ss工具来统计一下当前网络栈的内存使用情况 环境准备 组件 版本 操作系统 Ubuntu 22.04.4 LTS 查看socket内存相关参数,-m 参数 ss -tm State ...

  10. IOS多线程之NSOperation(2)

    IOS多线程之NSOperation(2) 最大并发数 open var maxConcurrentOperationCount: Int 并发数就是同时执行的任务数.比如,同时开3个线程执行3个任务 ...