现在的位置: 首页 > 综合 > 正文

CH Round#48 T2 4和7

2018年01月14日 ⁄ 综合 ⁄ 共 1686字 ⁄ 字号 评论关闭

神犇告诉了做法最后还是跪掉……好多细节问题……这道题一个朴素的想法当然是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;
}

抱歉!评论已关闭.