1. 程式人生 > >【集訓隊作業2018】【XSY3372】取石子 DP

【集訓隊作業2018】【XSY3372】取石子 DP

題目大意

  有 \(n\) 堆石子,初始時第 \(i\) 堆石子有 \(a_i\) 個。

  你每次取石子會取 \(k\) 個。在你取完一堆石子之後才能在下一堆中取石子。

  遊戲會進行 \(t\) 輪,每輪會發生以下事件:

  • 你可以進行任意次取石子操作。
  • 每堆的石子個數會增加,具體的,第 \(i\) 堆的式子個數會增加 \(b_i\) 個。

  每一堆式子有個上限 \(c_i\),如果在某個時刻,某堆石子的數量超過上限,就就輸了。

  求在不會輸掉遊戲的前提下,你最少進行幾次取石子操作。

  \(n,t\leq 200,1\leq k\leq {10}^9,0\leq a_i,b_i\leq c_i\leq {10}^9\)

題解

  我們可以在最後加一堆 \(a={10}^9,b={10}^9,c={10}^9\times (t+1)\) 的石子堆,這樣每次取石子都一定能取到 \(k\) 個。這可以讓我們更方便地計算石子個數。

  先考慮 \(a_i=0\) 的情況。

  記 \(sa,sb\)\(a,b\) 的字首和。

  記 \(f_{i,j}\) 為前 \(i\) 堆石子,進行了 \(j\) 輪遊戲,且每次取石子都取了 \(k\) 個的最小操作次數。

  記 \(g_{i,j}\) 為前 \(i\) 堆石子,進行了 \(j\) 輪遊戲,再取了若干次石子,每次石子都取了 \(k\) 個,且 \([1,i)\)

的石堆中沒有石子的最小操作次數。

  • 如果不取第 \(i\) 種石子也滿足要求(即 \(j\times b_i\leq c_i\)\(f_{i-1,j} \neq \infty\)),轉移為

    • \(f_{i,j}\leftarrow f_{i-1,j}\)
    • \(g_{i,j}\leftarrow\lceil\frac{j\times sb_{i-1}}{k}\rceil\)(要求 \(\lceil\frac{j\times sb_{i-1}}{k}\rceil\times k\leq j\times sb_i\),因為要有足夠多的式子給你取)
  • 否則列舉最後一次取 \(i\)

    的時間 \(l\),我們的策略是:

    • 先在前 \(l\) 輪取完 \([0,i)\),再取若干次石子:\(g_{i,l}\)
    • 計算要取多少次第 \(i\) 堆的石子:剩餘的石子個數是 \(m=l\times sb_{i}-k\times g_{i,l}\)。為了讓第 \(i\) 堆的石子不超過上限,我們還要取 \(x=\lceil\frac{\max(0,m+(j-l)\times b_i-c_i)}{k}\rceil\) 次。如果石子不夠(\(x\times k>m\)),則無解。
    • 再決策剩下 \(j-l\) 輪。這部分的貢獻和第一種情況類似。

    因此,轉移為:

    • \(f_{i,j}\leftarrow g_{i,l}+x+f_{i-1,j-l}\)
    • \(g_{i,j}\leftarrow g_{i,l}+x+\lceil\frac{(j-l)\times sb_{i-1}}{k}\rceil\)

  時間複雜度為 \(O(nt^2)\)

  \(a_i\neq 0\) 的情況和 \(a_i=0\) 的情況類似,只需要在某些計算石子個數的地方加上 \(a_i\) 即可。

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
void upmin(ll &a,ll b)
{
    a=min(a,b);
}
const int N=210;
const ll inf=0x3fffffffffffffffll;
ll f[N][N][2];
ll g[N][N][2];
ll a[N],b[N],c[N],sa[N],sb[N];
ll ceil(ll a,ll b)
{
    return (a+b-1)/b;
}
int n,t;
ll k;
int main()
{
    open("c");
    scanf("%d%d%lld",&n,&t,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        sa[i]=sa[i-1]+a[i];
        sb[i]=sb[i-1]+b[i];
    }
//  printf("%lld\n",sa[n]);
    n++;
    a[n]=1000000000ll;
    b[n]=1000000000ll;
    c[n]=1000000000ll*(t+1);
    sa[n]=sa[n-1]+a[n];
    sb[n]=sb[n-1]+b[n];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=t;j++)
        {
            f[i][j][0]=g[i][j][0]=inf;
            if(0*a[i]+j*b[i]<=c[i]&&f[i-1][j][0]!=inf)
            {
                upmin(f[i][j][0],f[i-1][j][0]);
                if(ceil(j*sb[i-1]+0*sa[i-1],k)*k<=0*sa[i]+j*sb[i])
                    upmin(g[i][j][0],ceil(j*sb[i-1]+0*sa[i-1],k));
            }
            for(int l=1;l<j;l++)
                if(f[i-1][j-l][0]!=inf&&g[i][l][0]!=inf)
                {
                    ll m=0*sa[i]+l*sb[i]-k*g[i][l][0];
                    ll x=ceil(max(0ll,m+(j-l)*b[i]-c[i]),k);
                    if(__int128(k)*x>m)
                        continue;
                    upmin(f[i][j][0],g[i][l][0]+x+f[i-1][j-l][0]);
                    if(ceil((j-l)*sb[i-1],k)*k<=m-x*k+(j-l)*sb[i])
                        upmin(g[i][j][0],g[i][l][0]+x+ceil((j-l)*sb[i-1],k));
                }
        }
    for(int i=1;i<=n;i++)
        for(int j=0;j<=t;j++)
        {
            f[i][j][1]=g[i][j][1]=inf;
            if(1*a[i]+j*b[i]<=c[i]&&f[i-1][j][1]!=inf)
            {
                upmin(f[i][j][1],f[i-1][j][1]);
                if(ceil(j*sb[i-1]+1*sa[i-1],k)*k<=1*sa[i]+j*sb[i])
                    upmin(g[i][j][1],ceil(j*sb[i-1]+1*sa[i-1],k));
            }
            for(int l=0;l<j;l++)
                if(f[i-1][j-l][0]!=inf&&g[i][l][1]!=inf)
                {
                    ll m=1*sa[i]+l*sb[i]-k*g[i][l][1];
                    ll x=ceil(max(0ll,m+(j-l)*b[i]-c[i]),k);
                    if(__int128(k)*x>m)
                        continue;
                    upmin(f[i][j][1],g[i][l][1]+x+f[i-1][j-l][0]);
                    if(ceil((j-l)*sb[i-1],k)*k<=m-x*k+(j-l)*sb[i])
                        upmin(g[i][j][1],g[i][l][1]+x+ceil((j-l)*sb[i-1],k));
                }
        }
    ll ans=f[n][t][1];
    printf("%lld\n",ans);
    return 0;
}