F. Mattress Run 题解
F. Mattress Run
挺好的一道题,对于DP的本质的理解有很大的帮助。
首先要想到的就是将这个拆成两个题,一个dp光求获得足够的夜晚的最小代价,一个dp光求获得足够的停留的最小代价。
显然由于这个题需要存储的信息很大,我们设状态时就要思考如何才能在规定的时间内完成而不超时。
请求有5000个,若设与请求有关的状态,例如:我们设f[i][j],表示当前在请求i,获得的资格j的最小代价,确实,因为我们知道了请求i的所有信息,包括结束时间,所在的旅馆号的信息,但在状态转移时,难免要枚举上一次的请求所在的位置,50005000,别忘了还有j的枚举再50,这样就爆了,别报侥幸心理,这个题输出方案加上你dp要做两边,常数巨大。那么考虑重新设计状态,考虑到转移时,实际上若上一次的天数不为a[i].l,我们根本不用在意他在哪个旅馆,当上一次的天数恰好为a[i].l时,我们才考虑他的旅馆。那么我们可以将天数和旅馆都设在状态里,设f[i][j][k]表示在第i天在旅馆j获得的资格为k的最小代价。先枚举请求,然后考虑每个请求会使得哪些状态发生改变,状态转移时,为了优化,我们令设dp[i][j]表示在第i天获得资格j的最小代价,方便我们省去枚举的旅馆数,之后我们另外讨论天数为a[i].l的情况即可。复杂度为500036550,轻松跑过,输出方案的话,我们大可用结构体来记录当前状态由哪一个转态得来。之后逆推一下即可。
#include<bits/stdc++.h>
using namespace std;
int n,s[2],h,m,c[5005],num;
int f[370][52][52][2],dp[370][52][2];
struct ss{int x,y,z;}Z[370][52][52][2],S[370][52][2];
//设f[i][j][k]表示第i天在j号旅馆获得的资格为k的最小代价,
//dp[i][j]表示第i天获得的资格为j的最小代价。
struct wy{int l,r,val,id,qid;}a[5005];
inline bool cmp(wy a,wy b)
{
return a.l<b.l;
}
inline void solve(int op)
{
f[0][0][0][op]=0;dp[0][0][op]=0;
for(int i=1;i<=m;++i)//枚举所有的请求。
{//转移来的天数分为两类,一类小于l,一类等于l
for(int j=0;j<a[i].l;++j)//枚举所有可以改变的状态的天数
for(int k=0;k<=s[op];++k)
{
int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]);
if(dp[j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op])
{
f[a[i].r][a[i].id][d][op]=dp[j][k][op]+a[i].val;
Z[a[i].r][a[i].id][d][op]=S[j][k][op];
}
if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op])
{
dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op];
S[a[i].r][d][op]={a[i].r,a[i].id,d};
}
}
for(int j=0;j<=h;++j) //等于l的,枚举从那个旅馆过来
{
if(j==a[i].id) continue;
for(int k=0;k<=s[op];++k) //枚举上一次的状态。
{
int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]);
if(f[a[i].l][j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op])
{
f[a[i].r][a[i].id][d][op]=f[a[i].l][j][k][op]+a[i].val;
Z[a[i].r][a[i].id][d][op]=(ss){a[i].l,j,k};
}
if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op])
{
dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op];
S[a[i].r][d][op]={a[i].r,a[i].id,d};
}
}
}
}
}
inline void work(ss x,int op)
{
if(x.x==0||x.y==0||x.z==0) return;
for(int i=1;i<=m;++i)
{
if(a[i].r==x.x&&a[i].id==x.y)
{
ss k=Z[x.x][x.y][x.z][op];
int d=min((op==0?k.z+a[i].r-a[i].l:k.z+1),s[op]);
if(d==x.z&&f[x.x][x.y][x.z][op]-a[i].val==f[k.x][k.y][k.z][op])
{
c[++num]=a[i].qid;
work(k,op);
return;
}
}
}
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d%d%d%d%d",&n,&s[0],&s[1],&h,&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&a[i].id,&a[i].l,&a[i].r,&a[i].val);
a[i].qid=i;
}
sort(a+1,a+m+1,cmp);
memset(f,0x3f,sizeof(f));
memset(dp,0x3f,sizeof(dp));
solve(0);solve(1);
int ans1=1e9,ans2=1e9,o1,o2;
for(int i=0;i<=n;++i)
{
if(dp[i][s[0]][0]<ans1)
{
ans1=dp[i][s[0]][0];
o1=i;
}
if(dp[i][s[1]][1]<ans2)
{
ans2=dp[i][s[1]][1];
o2=i;
}
}
if(min(ans1,ans2)==1e9) {puts("IMPOSSIBLE");return 0;}
else if(ans1<=ans2) work(S[o1][s[0]][0],0),puts("NIGHTS");
else if(ans1>ans2) work(S[o2][s[1]][1],1),puts("STAYS");
printf("%d\n",num);
for(int i=num;i>=1;--i) printf("%d ",c[i]);
return 0;
}
人一旦有了自信,就拥有了一切!
F. Mattress Run 题解的更多相关文章
- Codeforces Round #624 (Div. 3) F. Moving Points 题解
第一次写博客 ,请多指教! 翻了翻前面的题解发现都是用树状数组来做,这里更新一个 线段树+离散化的做法: 其实这道题是没有必要用线段树的,树状数组就能够解决.但是个人感觉把线段树用熟了会比树状数组更有 ...
- LuoguP5139 z小f的函数 题解
Content 给定 \(T\) 个二次函数 \(y=ax^2+bx+c\),有若干次操作,有一个操作编号 \(p\),保证仅为以下这五种: 操作 \(1\):给定 \(k\),将函数图像向上移动 \ ...
- HDU X mod f(x)(题解注释)
X mod f(x) Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 合肥学院ACM集训队第一届暑假友谊赛 B FYZ的求婚之旅 D 计算机科学家 F 智慧码 题解
比赛网址:https://ac.nowcoder.com/acm/contest/994#question B FYZ的求婚之旅 思路: 然后用快速幂即可. 细节见代码: #include <i ...
- Grazing on the Run 题解
[题目大意] 大致题意就是,你的初始坐标为\(x\),你要去数轴上的\(n\)个点,问你到达所有点的时间总和最小是多少. 直接贪心肯定不行,所以考虑\(DP\) 先把坐标离散(也就是预处理两点距离\( ...
- 2018年第九届蓝桥杯【C++省赛B组】B、C、D、F、G 题解
B. 明码 #STL 题意 把每个字节转为2进制表示,1表示墨迹,0表示底色.每行2个字节,一共16行,布局是: 第1字节,第2字节 第3字节,第4字节 .... 第31字节, 第32字节 给定一段由 ...
- LuoguB2147 求 f(x,n) 题解
Content 求给定 \(x,n\),求 \(f(x,n)=\sqrt{n+\sqrt{(n-1)+\sqrt{(n-2)+\sqrt{\dots+2+\sqrt{1+x}}}}}\) 的值. So ...
- 【AtCoder】ARC097 (C - F)题解
C - K-th Substring 题解 找出第K大的子串,重复的不计入 这个数据范围可能有什么暴力可以艹过去吧,但是K放大的话这就是后缀自动机板子题啊= = 代码 #include <ios ...
- BZOJ 一句话题解
菜鸡刷题记录 [题号:题解] 1008:简单排列组合 #include <bits/stdc++.h> using namespace std; #define ll long long ...
随机推荐
- 详解Java中==和equals()的区别
众所周知,在 Java 编程中,程序员通常会使用==或equals()来简单的比较地址,内容是否相等.而这两者之间的使用区别,对于初学 Java 的同学来说可能会比较迷糊.我将根据下面的几段示例程序, ...
- Linux没有/var/log/messages日志文件
1.新安装的CentOS8没有/var/log/messages日志文件: 安装rsyslog: dnf install -y rsyslog 或 yum install -y rsys ...
- mysql更新数据时:当想mysql某插入有某字段设置了unique且和之前相同时,会报错,并停止运行
这个在mysql5.7会报错: 如openid设为unique: 1062 - Duplicate entry 'oTfYq6PKne00IrcTqphmKqKnsahM' for key 'qx_w ...
- google插件网页播放mp4代码
<script src="http://html5media.googlecode.com/svn/trunk/src/html5media.min.js"></ ...
- Java基础系列(15)- 用户交互Scanner
Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入.java.util.Scanner是Java5的新特征.我们可以 ...
- javascript/html 禁止图片缓存
更新图片, 如果图片的url没有改变, 刷新页面之后图片会使用缓存的图片 Solutions: * js改变图片链接 (添加get参数) // 假设当前这个图片的dom对象为img img.src + ...
- php-抽象工厂
目标:创建有依赖关系的实例;(套餐) <?php //抽象类 食物 interface IAllayFood { function Allay(); } interface IDrinkFood ...
- JavaScript --css样式
1.JavaScript显示隐藏控制 隐藏:display:none; 显示:display:block; 参考链接:https://blog.csdn.net/sleepwalker_1992/ar ...
- Hibernate 的 <= 出现问题
问题模拟 select new map( e.name as name , e.salary as salary) from Emplpyee e where e.salary <= :sala ...
- 架构师必备:MySQL主从同步原理和应用
日常工作中,MySQL数据库是必不可少的存储,其中读写分离基本是标配,而这背后需要MySQL开启主从同步,形成一主一从.或一主多从的架构,掌握主从同步的原理和知道如何实际应用,是一个架构师的必备技能. ...