烟火
城镇的主干道上有n个区域,从左到右编号为1到n,每个区域之间相距1个单位距离。在节日中要放m个烟火,第i个烟火会在ti时刻的ai区域放。如果在ti时刻你所处区域为x,那么你可以获得bi - | ai - x |的快乐值。在每个单位时间你可以移动不超过d个单位距离,初始的位置是任意的,求通过移动能获得快乐值和的最大值。
1 <= n <= 150000,1 <= m <= 300,1 <= d, ai <= n,1 <= bi, ti <= 10^9,对于i >= 2有ti >= ti-1。
题解
状态设计很简单f[i][j]表示看了i个烟火,在j的位置的最大快乐值。
f[i][j]=max{f[i-1][k]}+b[i]-|a[i]-j|
k是一段连续的区间,并且对于同一个i长度固定,所以可以用单调队列维护,注意可以双向移动。
注意pos开long long
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=150005; const ll inf=1000000000000ll; int n,m; ll d; ll f[2][maxn]; int a[maxn],b[maxn],T[maxn]; int h,t,q[maxn]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } ll abs(ll x){return x<0 ? -x : x ;} ll min(ll x,ll y){return x<y ? x : y ;} int main(){ freopen("fireworks.in","r",stdin); freopen("fireworks.out","w",stdout); read(n);read(m);read(d); for(int i=1;i<=m;i++) read(a[i]),read(b[i]),read(T[i]); for(int i=1;i<=n;i++) f[1][i]=b[1]-abs(a[1]-i); for(int i=2;i<=m;i++){ h=1,t=0; int now=i&1,pre=now^1; for(int j=1;j<=min(n,1+d*(T[i]-T[i-1]));j++){ while(h<=t&&f[pre][j]>f[pre][q[t]]) t--; q[++t]=j; } for(int j=1;j<=n;j++){ while(h<=t&&abs(j-q[h])>d*(T[i]-T[i-1])) h++; int p=q[h]; f[now][j]=f[pre][p]+b[i]-abs(a[i]-j); ll pos=j+1+(ll)d*(T[i]-T[i-1]); if(pos<=n){ while(h<=t&&f[pre][pos]>f[pre][q[t]]) t--; q[++t]=pos; } } } ll mx=-inf; for(int i=1;i<=n;i++) mx=max(mx,f[m&1][i]); printf("%lld",mx); } /* 10 2 1 1 1000 2 9 1000 4 */
购物
商店里有n个物品,第i个物品的价格为ci元。每个物品只能买一次。商店发行了n张优惠券,每个物品各有一张优惠卷。如果使用了第i张优惠券,可以使该物品便宜di元钱,必须买商品才能够使用相对应的优惠券。第1张优惠卷可以无条件使用,但对于第i>=2张优惠卷,如果需要使用第i张优惠券,则必须先使用xi这张优惠券。
现在有b元钱,问最多能购买多少商品。
1 <= n <= 5000,1 <= b <= 10^9,1 <= di < ci <= 10^9,对于i >= 2有1 <= xi < i。
题解
显然商品构成了树形结构。
f[i][j][0/1]表示i的子树中选取j个商品,不使用/使用i的优惠券所花的最少费用。
最会找最多的商品并且费用<=b
转移比较好想,但是因为这道题卡空间,但是因为b<=1e9,所以大于1e9的都不用考虑。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=5005; const ll oo=1000000001; int n,m; int c[maxn],d[maxn]; int f[maxn][maxn][2]; int size[maxn]; int cnt,head[maxn]; struct edge{ int x,y,next; }e[maxn]; void add(int x,int y){ e[++cnt]=(edge){x,y,head[x]}; head[x]=cnt; } template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } ll min(ll x,ll y){return x<y ? x : y ;} void dfs(int x){ size[x]=1; f[x][1][0]=c[x]; f[x][1][1]=c[x]-d[x]; f[x][0][0]=0; for(int i=head[x];i;i=e[i].next){ int y=e[i].y; dfs(y); for(int j=size[x];~j;j--) for(int k=1;k<=size[y];k++){ f[x][j+k][0]=min(f[x][j+k][0],f[x][j][0]+f[y][k][0]); f[x][j+k][1]=min(f[x][j+k][1],min(f[y][k][0],f[y][k][1])+f[x][j][1]); } size[x]+=size[y]; } } int main(){ freopen("shopping.in","r",stdin); freopen("shopping.out","w",stdout); read(n);read(m); read(c[1]);read(d[1]); for(int i=2;i<=n;i++){ read(c[i]);read(d[i]); int x;read(x); add(x,i); } memset(f,0x3f,sizeof(f)); cnt=0; dfs(1); for(int i=n;~i;i--) if(min(f[1][i][1],f[1][i][0])<=m) {printf("%d",i);return 0;} } /* 8 9 4 3 8 3 1 2 1 1 4 2 2 7 2 2 3 1 2 7 3 5 2 1 3 */
括号匹配
给出长度为N的括号序列(只包含(,),[,]),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。
100%的数据保证:1 <= N <= 300。
题解
考虑在删除一对合法的括号之后,剩下的两个部分的方案数相乘为总方案数(乘法原理)。
对于当前区间,最左边的可以匹配或者不匹配,递归搜索即可。
记忆化搜索
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int mod=1000000007; const int maxn=305; int n; ll f[maxn][maxn][2];//[l,r]是否出现了合法序列 char s[maxn]; bool cx(int l,int r){return (s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']'); } ll dfs(int l,int r,bool opt){ if(f[l][r][opt]!=-1) return f[l][r][opt]; if(l>=r) return opt; ll ret=dfs(l+1,r,opt)%mod;//不进行匹配 if(s[l]=='('||s[l]=='['){ for(int i=l;i<=r;i++) if(cx(l,i)) ret=(ret+dfs(l+1,i-1,1)*dfs(i+1,r,1)%mod)%mod; } return f[l][r][opt]=ret; } int main(){ freopen("parenthesis.in","r",stdin); freopen("parenthesis.out","w",stdout); scanf("%d%s",&n,s+1); memset(f,-1,sizeof(f)); printf("%lld",dfs(1,n,0)); }