青蛙
题目描述
有n 块石头排成一行,从左到右依次编号为1到n,相邻两块石头之间的距离为1。
有m 只青蛙,开始时都位于1 号石头,它们都需要跳到n 号石头上。青蛙只能跳到更靠右的石头上。如果第i 只青蛙的某次跳跃的距离超过d,那么需要付出ci的代价。
求出满足以下两个条件时,总代价的最小值:
(1)石头a1,a2,a3,…,ak 必须被跳到恰好一次。
(2)其它石头(不包含1号石头和n号石头)不能被跳到。
有多组数据。
输入格式
第一行一个整数t表示数据组数。
每组数据第一行四个整数n,m,k,d,第二行m个整数c1~cm,第三行k个整数a1~ak。
输出格式
每组数据输出一行一个整数表示答案。
样例输入
2
10 2 3 3
4 7
4 8 7
10 2 2 3
4 7
9 5
样例输出
4
15
题解
贪心。
把ci排序。
为保证总代价最小,ci越小的青蛙付出代价的次数要越少。ci大的青蛙,能不付出代价最好。
先计算可以踩的相邻两块石头之间的距离,如果每两块石头间的距离都不超过d,就一定有青蛙可以不花代价跳到终点。计算有多少青蛙可以不花代价跳到终点,让ci最大的几只青蛙跳过去。
怎么算呢?题解有3种方法:
首先求出最多能有多少只青蛙不花费代价到达 n,记为 p。
方法一:考虑每相邻 d 块石头的可以经过次数之和,p 为这个的最小值。
方法二:二分答案,维护青蛙的位置,每次有空石头时将最靠左的跳过去。
方法三:贪心,对于每只青蛙,不停选择能跳的最靠右的石头跳过去。
我打的是方法一,这里再解释一下:
n块石头有的不能踩,有的能踩,“每相邻 d 块石头的可以经过次数之和”就是指每相邻d块石头中可以踩的石头数。每相邻 d 块石头为一组,因为青蛙不花代价可以跳的最大距离为d,每组青蛙跳到相邻的下一组都不用花代价。如果每块石头站一只青蛙,这些青蛙都可以不花代价跳到下一组。但是每组可以站的青蛙数不同,取最小值。c 最大的 p 只青蛙不花费代价,其余青蛙分别花费一次代价直接从 1 跳到 n。
如果存在两块相邻可踩石头近的距离大于d,那么所有青蛙都要花费代价,最优策略是 c 最小的一只青蛙跳过这 k 块石头,其余青蛙直接从 1 跳到 n。
注意!!! 题目没说石头a1,a2,a3,…,ak是有序的,记得排序!
#include <algorithm> #include <cstdio> int T,l,n,m,d,a[100005]; long long c[100005],ans; int main() { // freopen("frog.in","r",stdin); // freopen("frog.out","w",stdout); int i,j,s; long long t; scanf("%d",&T); while (T--) { scanf("%d%d%d%d",&l,&m,&n,&d); for (i=1;i<=m;i++) scanf("%lld",&c[i]); for (i=1;i<=n;i++) scanf("%d",&a[i]); std::sort(c+1,c+m+1); std::sort(a+1,a+n+1); a[0]=1; a[n+1]=l; ans=0; for (i=1,t=0;i<=n+1;i++) if (a[i]-a[i-1]>d) t++; if (t==0) { for (i=1,j=1,s=m;i<=n;i++) { while (j<=n+1 && a[j]-a[i-1]<=d) j++; if (j<=n) { if (j-i<s) s=j-i; } else break; } for (i=1;i<=m-s;i++) ans+=c[i]; } else { ans=t*c[1]; for (i=2;i<=m;i++) ans+=c[i]; } printf("%lld\n",ans); } return 0; }