神犇告诉了做法最后还是跪掉……好多细节问题……这道题一个朴素的想法当然是O(m)的DP……但是m的范围告诉你想要100分必须得想与m无关的算法……通过打表发现(= =囧)……从第18个格子开始,都是可以从起点0处出发到达的……所以只需记录下前17个格子是否能达到(即该数能否由4和7组成),这里我用c数组储存打好的表,c[i]代表第i个格子能否到达。
然后对读入的数据以格子的先后为关键字排序(显然题目保证b[i]都不相同)
接着对这n堆药扫一遍。对于第i堆药,再来从后往前枚举,如果找到b[i]-b[j]<18的就判断能否转移,然后更新f[i]的值。
直到发现b[i]-b[j]>=18,这时候再前面的的肯定是可以转移的。
那么我们定义mx[i]为f[1]到f[i]中的最大值,即mx[i]=max(f[1],f[2]...f[i])。那么此时只需要取mx[j]来更新即可。
注意到i是从前往后枚举,那么只需要在每次i++以后更新mx[i]即可,这样可以保证之后的枚举i时,之前的j(0<=j<=i)是存在mx[j]的。。。
还要注意一个特判(就是我SB的地方)。如果前面b[i]-b[j]<18的枚举完了,发现j为0时。这时候并不是b[i]-b[j]>=18导致的了。。。所以这时候必须要判断j能否到达i,或者b[i]-b[j]>=18两者满足其一即可。不然就不能更新……
然后因为最后停下来的位置可以是任意位置,所以输出mx[n]。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<string> #include<set> #include<queue> #include<stack> #include<map> #include<cmath> #include<cstdlib> #define ll long long #define maxn 100010 #define inf 1000000000 #define linf (1LL<<50) using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();} return x*f; } inline void read(char *s,int &ts) { char x=getchar(); while(!(x>='a'&&x<='z'))x=getchar(); while(x>='a'&&x<='z')s[++ts]=x,x=getchar(); } struct data{ int a,b; }e[maxn]; int f[maxn]; int n,m,q,ans; int c[18]={1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,0}; int mx[maxn]; bool cmp(data a,data b) { return a.b<b.b; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&e[i].a,&e[i].b); sort(e+1,e+n+1,cmp); for(int i=1;i<=n;i++) { int j; for(j=i-1;j&&e[i].b-e[j].b<18;j--) if(c[e[i].b-e[j].b]) f[i]=max(f[i],f[j]+e[i].a); if(j>0) f[i]=max(f[i],mx[j]+e[i].a); else if((j==0&&c[e[i].b])||e[i].b>=18) f[i]=max(f[i],mx[j]+e[i].a); mx[i]=max(f[i],mx[i-1]); } printf("%d\n",mx[n]); return 0; }