1. 程式人生 > >APIO2015巴厘島的雕塑——數位DP

APIO2015巴厘島的雕塑——數位DP

判斷 答案 HR mes != www. tdi turn 枚舉

題目:https://www.luogu.org/problemnew/show/P3646

對於A>1,將答案各位全置1,然後從高位到低位改成0判斷是否可行;

用f[i][j]數組代表前i個數分成j組是否可行,轉移是枚舉最後一段的左端點k,然後看看後面整個一段的和能否滿足要求,如果前後都滿足就表示i,j狀態也可行;

對於A=1,可以貪心地認為分組數量越少越好,所以可行性轉化為最優性,省去一維,轉移條件同上,取min即可;

先寫了個WA一半的版本:

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstring>
using
namespace std; typedef long long ll; int n,A,B,len; ll f2[2005],ans,s[2005]; bool f[105][105];//可行性 bool dp1(ll x) { memset(f,0,sizeof f); f[0][0]=1; for(int i=1;i<=n;i++)//前i個數分成j段 <- 前k個數分成j-1段 for(int j=1;j<=i;j++) for(int k=0;k<i;k++)//0 if(((s[i]-s[k])|x)==x)f[i][j]|=f[k][j-1
]; for(int i=A;i<=B;i++) if(f[n][i])return 1; return 0; } bool dp2(ll x) { // memset(f2,0x3f,sizeof f2); f2[0]=0; for(int i=1;i<=n;i++) { ll ad=n+1; for(int j=0;j<i;j++)//0 if(((s[i]-s[j])|x)==x)ad=min(ad,f2[j]); f2[i]=ad+1; }
return f2[n]<=B; } int main() { scanf("%d%d%d",&n,&A,&B); for(int i=1;i<=n;i++) scanf("%lld",&s[i]); for(int i=1;i<=n;i++) s[i]+=s[i-1]; for(len = 0;(1LL << len) <= s[n];len++);len--;//位數 if(A!=1) { ans=(ll)(1<<(len+1));ans--; for(int k=len;k>=0;k--)//0! { ll tmp=ans-(ll)(1<<k); if(dp1(tmp))ans=tmp; } } else { ans=(ll)(1<<(len+1));ans--; for(int k=len;k>=0;k--) { ll tmp=(ll)ans-(1<<k); if(dp2(tmp))ans=tmp; } } printf("%lld",ans); return 0; }

後來又直接改成別的寫法A的,但還是不太明白原來的寫法為什麽不行,有什麽不同。

代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,A,B,len;
ll f2[2005],ans,s[2005];
bool f[105][105];//可行性
ll dp1()
{
    ans=0;
    for(int t=len;t>=0;t--)
    {
        ans+=(1LL<<t)-1;
        memset(f,0,sizeof f);
        f[0][0]=1;
        for(int i=1;i<=n;i++)//前i個數分成j段 <- 前k個數分成j-1段 
            for(int j=1;j<=i;j++)
                for(int k=0;k<i;k++)//0
                    if(((s[i]-s[k])|ans)==ans)f[i][j]|=f[k][j-1];
        bool fl=0;
        for(int i=A;i<=B;i++)fl|=f[n][i];
        if(fl)ans-=(1LL<<t)-1;
        else ans++;
    }
    return ans;
} 
ll dp2()
{
    ans=0;
    for(int t=len;t>=0;t--)
    {
        ans+=(1LL<<t)-1;
        f2[0]=0;
        for(int i=1;i<=n;i++)
        {
            ll ad=n+1;
            for(int j=0;j<i;j++)//0
                if(((s[i]-s[j])|ans)==ans)ad=min(ad,f2[j]);
            f2[i]=ad+1;
        }
        if(f2[n]<=B)ans-=(1LL<<t)-1;
        else ans++;
    }
    return ans;
}
int main()
{
    scanf("%d%d%d",&n,&A,&B);
    for(int i=1;i<=n;i++)
        scanf("%lld",&s[i]);
    for(int i=1;i<=n;i++)
        s[i]+=s[i-1];
    for(len = 0;(1LL << len) <= s[n];len++);len--;//位數 
    if(A==1)printf("%lld",dp2());
    else printf("%lld",dp1());
    return 0;
}

APIO2015巴厘島的雕塑——數位DP