T1:小奇挖矿 2(mining):
【题目背景】
小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光
之源的矿石交易市场,以便为飞船升级无限非概率引擎。
【问题描述】
现在有 m+1个星球,从左到右标号为 0到m,小奇最初在0号星球。
有n 处矿体,第i 处矿体有ai单位原矿,在第 bi个星球上。 //attention:矿石可能在同一个星球上--so w[b]+=a not w[b]=a--因为这个少了30分
由于飞船使用的是老式的跳跃引擎,每次它只能从第 x 号星球移动到第 x+4
号星球或x+7号星球。每到一个星球,小奇会采走该星球上所有的原矿,求小奇
能采到的最大原矿数量。
注意,小奇不必最终到达 m号星球。
【输入格式】
第一行2个整数n,m。
接下来n行,每行2个整数 ai,bi。
【输出格式】
输出一行一个整数,表示要求的结果。
【样例输入】
3 13
100 4
10 7
1 11
【样例输出】
101
【样例解释】
第一次从0到4,第二次从 4到11,总共采到 101单位原矿。
【数据范围】
对于20%的数据 n=1,m<=10^5
对于40%的数据 n<=15,m<=10^5
对于60%的数据 m<=10^5
对于100%的数据 n<=10^5,m<=10^9,1<=ai<=10^4,1<=bi<=m
题解:
小奇挖矿 2
1.对于 20%的数据,n=1。只需判断这个星球位置是否能拆分成 4和7 的和。
可以简单递推,枚举求得。
打表的话可以发现,只有 1 2 3 5 6 9 10 13 17 不能拆。
2.对于 40%的数据,n 很小。可以 2^n 枚举去哪些星球,然后用上面的方法判
定一个星球能否到下一个星球。
3.对于 60%的数据,m比较小。设 x[i]为i号星球的原矿数,f[i]为到 i号星
球能获得的最大原矿数,则 f[i]=max{f[i-4],f[i-7]}+x[i]。 //自己的思想
4.对于 100%的数据,m很大。
a.将所有矿物排序,f[i]为到第 i 个矿体能获得的最大原矿数,那么
f[i]=max{f[j]}+x[i],其中,i-j满足能拆分成 4和7。
这样 dp需要n^2 的复杂度,但是我们发现,相距超过 17的一定可达,那么
不超过17的范围内暴力,超过 17的取个最大值即可,复杂度 O(n)
b.可以把相邻的星球距离超过 17的压缩成 18,按照60%的做法dp
代码:
1 #include<map>
2 #include<set>
3 #include<cmath>
4 #include<cstdio>
5 #include<cstring>
6 #include<algorithm>
7 #include<iostream>
8 #define inf 1000000000
9 #define ll long long
10 using namespace std;
11 ll read()
12 {
13 ll x=0,f=1;char ch=getchar();
14 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
15 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
16 return x*f;
17 }
18 struct data{
19 int a,b;
20 friend bool operator<(data a,data b){
21 return a.a<b.a;//结构体重载运算符
22 }
23 }a[100005];
24 int n,m;
25 int v[2000005],f[2000005];
26 int main()
27 {
28 freopen("mining.in","r",stdin);
29 freopen("mining.out","w",stdout);
30 memset(f,-1,sizeof(f));
31 n=read();m=read();
32 for(int i=1;i<=n;i++)
33 a[i].b=read(),a[i].a=read();
34 sort(a+1,a+n+1);
35 int now=0;
36 for(int i=1;i<=n;i++)
37 {
38 if(a[i].a-a[i-1].a>17)now+=18,v[now]+=a[i].b;
39 else now+=a[i].a-a[i-1].a,v[now]+=a[i].b;
40 }
41 int ans=0;
42 f[0]=0;
43 for(int i=0;i<=now;i++)
44 if(f[i]!=-1)
45 {
46 f[i+4]=max(f[i+4],f[i]+v[i+4]);
47 f[i+7]=max(f[i+7],f[i]+v[i+7]);
48 ans=max(ans,f[now]);
49 }
50 printf("%d\n",ans);
51 return 0;
52 }
所获:
不得不说,信奥也还是要有数感。对于数字4 7得有感觉,两个质数、两个奇数等等对于后面代码和算法的优化极其重要,不然真的会无从下手。
比如这题,判断这个星球位置是否能拆分成 4和7 的和,发现,只有 1 2 3 5 6 9 10 13 17 不能拆,相距超过 17的一定可达,那么
不超过17的范围内暴力,超过 17的取个最大值即可。 这一点就是解题的关键。
但总体来说,这是一道DP题目,有点类似与硬币问题。