1. 程式人生 > >【Codeforces436D】Pudding Monsters

【Codeforces436D】Pudding Monsters

題意:

  • 開始有無限長的一段格子,有n個格子種有布丁怪獸,一開始連續的布丁怪獸算一個布丁怪獸。
  • 每回合你可以將一個布丁怪獸向左或右移動,他會在碰到第一個布丁怪獸時停下,並與其合併。
  • 如果最左邊的怪獸向左,你可以認為是將其移動到了無窮遠處。
  • m個特殊格子,詢問最終你最多可以讓幾個特殊的格子上被布丁覆蓋。
  • n10000,m2000

題解:

  • 首先考慮兩種狀態:f[i]表示前i個布丁(相同的布丁怪獸算長度個)最多覆蓋的特殊格子數,g[i]表示前i個布丁,第i個不動的情況下最多覆蓋的特殊格子數,sum(l,r)表示[l,r]區間中有多少個特殊格子。那麼答案就是f[n]
  • 考慮如何轉移:
  • 第一種:
  • 設當前轉移座標為r=a[i](當前列舉到第i個布丁)。
  • 列舉在r左邊的特殊格子位置l=b[j](b[j]<=a[i]),想要覆蓋[l,r]區間的所有特殊格子,就至少需要另外rl個布丁,即irl+1,那麼可以得到轉移方程1:
  • g[i]=max(g[i],f[i(rl)1]+sum(l,r))
  • 第二種:
  • 設當前轉移座標為l=a[i](當前列舉到第i個布丁)。
  • 列舉在l右邊的特殊格子位置r=b[j](b[j]<=a[i]),想要覆蓋[l,+1r]區間的所有特殊格子,就至少需要另外rl個布丁,即i+rln,那麼可以得到轉移方程2:
  • f[i+rl]=max(f[i+r
    l],g[i]+sum(l+1,r))
  • 第三種:
  • 顯然可以發現轉移方程3:
  • f[i]=max(f[i],f[i1]+[sum(a[i],a[i])==1])
  • g[i]=max(g[i],g[i1]+[sum(a[i],a[i])==1])
  • 然後我們發現這兩個陣列其實可以合併,一起轉移,於是就成功縮短了程式碼。
  • 注意到我們沒有考慮一個怪獸不只是佔了一格的情況,我們可以在原來轉移的基礎上引入L[i],R[i]表示第i個布丁所屬的怪獸的最左邊格子編號和最右邊格子編號,這樣就能解決上述情況。

程式碼:

#include <bits/stdc++.h>
#define gc getchar()
#define ll long long #define N 200009 #define M 2009 using namespace std; int n,m,a[N],b[M],sum[N],dp[N],L[N],R[N]; int read() { int x=1; char ch; while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1; int s=ch-'0'; while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0'; return s*x; } int main() { n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+n+1); a[0]=a[1]-1000,a[n+1]=a[n]+1000; for (int i=1;i<=n;++i) if (a[i]==a[i-1]+1) L[i]=L[i-1]; else L[i]=i; for (int i=n;i;--i) if (a[i]==a[i+1]-1) R[i]=R[i+1]; else R[i]=i; sum[0]=0; for (int i=1;i<=m;i++) b[i]=read(),sum[b[i]]++; sort(b+1,b+m+1); for (int i=1;i<=200000;i++) sum[i]+=sum[i-1]; for (int i=1;i<=n;i++) { dp[i]=max(dp[i],dp[i-1]); int Max; if (L[i]==i) Max=dp[i-1]+sum[a[i]]-sum[a[i]-1]; else Max=-10000000; dp[i]=max(dp[i],Max); for (int l=1;b[l]<=a[i]&&l<=m;l++) if (i-(a[i]-b[l])>=1) { dp[i]=max(dp[i],dp[L[i-(a[i]-b[l])]-1]+sum[a[i]]-sum[b[l]-1]); Max=max(Max,dp[L[i-(a[i]-b[l])]-1]+sum[a[i]]-sum[b[l]-1]); } for (int r=m;b[r]>=a[i]&&r;r--) if (i+b[r]-a[i]<=n) dp[R[i+b[r]-a[i]]]=max(dp[R[i+b[r]-a[i]]],Max+sum[b[r]]-sum[a[i]]); } printf("%d\n",dp[n]); return 0; }