题目描述
有 ???? 名同学要乘坐摆渡车从人大附中前往 名同学要乘坐摆渡车从人大附中前往 名同学要乘坐摆渡车从人大附中前往 名同学要乘坐摆渡车从人大附中前往 人民大学 ,第 ???? 位同学在第 ???????? 分钟去 等车 。只有一辆摆渡车在工作 ,但摆渡车容量可以视为无限大 ,但摆渡车容量可以视为无限大 。摆渡车从人大附中出发、 把车上的 车上的 同学送到人民大、再回附中 同学送到人民大、再回附中 同学送到人民大、再回附中 同学送到人民大、再回附中 同学送到人民大、再回附中 同学送到人民大、再回附中 同学送到人民大、再回附中 (去接其他同学 去接其他同学 ),这样往返一趟 ),这样往返一趟 ),这样往返一趟 ),这样往返一趟 总共花 总共花 费 ???? 分钟 (同学上下车时间忽略不计)。 (同学上下车时间忽略不计)。 摆渡车要将所有同学 都送到人民大摆渡车要将所有同学 都送到人民大。
凯很好奇,如果 凯很好奇,如果 他能任意安排 摆渡车出发的时间, 那么 这些同学的等车时间之和 最小为多少呢 ?
注意:摆渡车回到人大附中后可以即刻出发。
【输入 格式 】
输入文件名为 bus .in 。
第一行包含两个正整数 ????,m,以一个空格分开,别代表 ,以一个空格分开别代表 ,以一个空格分开别代表 等车人数和摆渡往返 一趟的时间。
第二行 包含 ???? 个正整数, 个正整数, 相邻两数之间 相邻两数之间 相邻两数之间 以一个 空格分隔, 空格分隔, 空格分隔, 第 i 个非负 整数 ???????? 代 表第 i 个同学到达车站的时刻。
【输出 格式 】
输出文件名为 bus .out 。
输出 一行,个整数表示 所有同学等车 时间 之和的最小值 (单位: 分钟) 。
【输入出样例 1】
bus .in
bus .out
5 1
3 4 5
0
见选手目录下的 bus /bus 1.in 和 bus /busbus 1.ans 。
【输入出样例 1说明】
同学 1 和同学 4 在第 3 分钟开始等车, 等待 0 分钟, 在第 3 分钟 乘坐摆渡车 乘坐摆渡车 出发。 摆渡车 在第 4 分钟回到 人大附中 。
同学 2 和同学 3 在第 4 分钟开始等车,待 0 分钟,在第 4 分钟乘坐摆渡车 分钟乘坐摆渡车 出发。摆渡车在第 5 分钟回到人大附中。
同学 5 在第 5 分钟开始等车,待 分钟开始等车,待 0 分钟,在第 分钟,在第 分钟,在第 5 分钟乘坐摆渡车出发。 自此 所有同学 都被送到人民大学 。总等待时间为 0。
【输入出样例 2】
bus .in
bus .out
5
11 13 1 5
4
见选手目录下的 bus /bus 2.in 和 bus /busbus 2.ans 。
【输入出样例 2说明】
同学 3 在第 1 分钟开始等车,待 分钟开始等车,待 0 分钟 ,在第 1 分钟乘坐摆渡 车出发。分钟乘坐摆渡 车出发。分钟乘坐摆渡 车出发。车在第 6 分钟回到人大附中。
同学 4 和同学 5 在第 5 分钟开始等车,待 1 分钟 ,在第 6 分钟乘坐摆渡车 分钟乘坐摆渡车 出发。摆渡车在第 11 分钟回到人大附中。
同学 1 在第 11 分钟开始等车,待 分钟开始等车,待 分钟开始等车,待 分钟开始等车,待 分钟开始等车,待 分钟开始等车,待 分钟开始等车,待 2 分钟;同学 分钟;同学 分钟;同学 分钟;同学 2 在第 13 分钟开始等车, 分钟开始等车, 分钟开始等车, 分钟开始等车, 分钟开始等车, 等待 0 分钟。他 分钟。他 /她们在第 13 分钟乘坐摆渡车出发。 分钟乘坐摆渡车出发。 自此所有同学都被送到人民大。 总等待时间为 4。可以证明,没有总等待时间小于 4 的方案。
【输入出样例 3】
见选手目录下的 bus /bus 3.in 和 bus /busbus 3.ans 。
【数据规模与约定】
对于 10% 的数据, ????≤10,????=1,0≤????????≤100。
对于 30% 的数据, ????≤20,????≤2,0≤????????≤100。
对于 50% 的数据, ????≤500,????≤100,0≤????????≤104。
另有 20% 的数据, ????≤500,????≤10,0≤????????≤4×106。
对于 100% 的数据, ????≤500,????≤100,0≤????????≤4×106。
没想到普及组的题也这么难呢(好吧,是我太弱了)。这道题一开始是打算打dp来着,但是dp的定义和状态转移方程过于难想,没有想到,在考试时就打了个爆搜想要得个部分分,然鹅还是因为我太弱了的原因,就在dfs时以时间作为状态,结果当然是时间妥妥地炸了,只得了10分(好像以每个人来深搜然后不加记忆化是可以得30分来着,然鹅我太蠢了没有想到hhh)
#include<bits/stdc++.h> using namespace std; int timer[503],tong[4000003]; int ans=210000000,n,m; void dfs(int t,int sum,int tot)//分别是时间,在这个时候多少人在等,已经等待了的时间和 { if(tot>=ans) return ;//剪枝 if(t>=timer[n]) //时间到最后一个人的到达时间就可以停止了 { ans=min(ans,tot); return ; } //下面就分为上车和不上车 //上车 sum=min(0,sum);//等待的人全部上车,所以sum清0 int pp=tot; for(int i=t+1;i<=t+m-1;i++) { if(tong[i]) sum+=tong[i],pp+=tong[i]*(m-i+t);//在车的来回m的时间内,可能又来了新的人,加入等待中 } dfs(t+m,sum,pp);//时间+m,注意时间和不要直接在tot上加减,不然回溯时可能出问题 //不上车 if(tong[t+1]) //下一刻有人到达 { sum+=tong[t+1];//加入等待 dfs(t+1,sum,tot+sum-tong[t+1]);//在t+1的这一时间新加入的人不对等待时间做出贡献,但又确实加入了等待 //所以tot减去tong[t+1](注意理解,主要是思想吧,有更好的打法也行啦) } else dfs(t+1,sum,tot+sum);//没有新的人到达 } int main() { //freopen("bus.in","r",stdin); //freopen("bus.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&timer[i]); tong[timer[i]]++;//开一个桶去存某个时间是不是某某的到达时间,有几个 } if(m==1){printf("0\n"); return 0;} sort(timer+1,timer+1+n);//排序 dfs(1,1,0); printf("%d",ans); } /* 5 1 3 4 4 3 5 5 5 11 13 1 5 5 */
然鹅大佬的人是这样想的:dfs(i,st)意思是第i个人在st时间时已经积累了的等待时间(具体看代码吧hhh)(借鉴了洛谷5017题解中chen_zhe大佬的代码)
#include<bits/stdc++.h> using namespace std; int read()//快读 { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } int n,m,t[505];//mem[i][j]第i个人等待j的时间(注意是等待的时间) //因为0<=st-t[i]<=m,因此可以记忆化,把这个作为状态的第二维 int solve(int i,int st)//记忆化搜索。i:第i个人,st:开始时间st { if (i==n+1)//所有人都上车了 return 0;//便不再有等待时间了 if (st<t[i])//如果现在的时间没有人,就到下一个人的到达时间 return solve(i,t[i]); //这一句和上一句的顺序不能换,不然数组越界会RE int sum=0,j=i;//j从i开始 //车等人 即车到的时间在这个人的后面 while (j<=n && t[j]<=st)//车一到,该上的人就上,就不再等了 sum+=t[j++];//这里j++所以下面是j,而再下面是j+1 int best=st*(j-i)-sum+solve(j,st+m);//i到j这一段的再加上后面的(可以看做是一个累加过程) //人等车 即人到的时间在车的后面 for (;j<=n;j++)//让车再等一下,多加后面的人上车 {//j一直枚举到n,连把人等完 sum+=t[j]; best=min(t[j]*(j-i+1)-sum+solve(j+1,t[j]+m),best); } return best; } int main() { //memset(mem,-1,sizeof(mem)); n=read(),m=read(); for (int i=1;i<=n;i++) t[i]=read(); sort(t+1,t+n+1);//显然从小到大按照时间排序更好算 printf("%d",solve(1,0)); return 0; }
这样仿佛就可以得30分啦啦啦
然后加一个记忆化就可以A掉这道题,下面是记忆化
#include<bits/stdc++.h> using namespace std; int read()//快读 { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } int n,m,t[505],mem[505][505];//mem[i][j]第i个人等待j的时间 //因为0<=st-t[i]<=m,因此可以记忆化,把这个作为状态的第二维 int solve(int i,int st)//记忆化搜索。i:第i个人,st:开始时间st { if (i==n+1)//所有人都上车了 return 0;//便不再有等待时间了 if (st<t[i])//如果现在的时间没有人,就到下一个人的到达时间 return solve(i,t[i]); //这一句和上一句的顺序不能换,不然数组越界会RE if (mem[i][st-t[i]])//记忆化 return mem[i][st-t[i]]; int sum=0,j=i;//j从i开始 //车等人 即车到的时间在这个人的后面 while (j<=n && t[j]<=st)//车一到,该上的人就上,就不再等了 sum+=t[j++];//这里j++所以下面是j,而再下面是j+1 int best=st*(j-i)-sum+solve(j,st+m);//i到j这一段的再加上后面的(可以看做是一个累加过程) //人等车 即人到的时间在车的后面 for (;j<=n;j++)//让车再等一下,多加后面的人上车 {//j一直枚举到n,连把人等完 sum+=t[j]; best=min(t[j]*(j-i+1)-sum+solve(j+1,t[j]+m),best); } return mem[i][st-t[i]]=best; } int main() { //memset(mem,-1,sizeof(mem)); n=read(),m=read(); for (int i=1;i<=n;i++) t[i]=read(); sort(t+1,t+n+1);//显然从小到大按照时间排序更好算 printf("%d",solve(1,0)); return 0; }
总之,这道题主要是注意理解思想,特别是人等车和车等人那部分要取min,然后dp的做法我还没看呢(hhh)
有什么问题欢迎指正
(纪念一下第一篇博客嘻嘻嘻)