5322. 【GDOI2017模拟8.21】小朋友 状压dp

92 篇文章 1 订阅
20 篇文章 0 订阅

这里写图片描述
n<=1000,3<=k<=10,ai<=100000
状压dp的题目,据出题人说是裸题,奈何我状压一直是最弱的= =,问了ymwdalao才知道怎么打。。讲道理基本上超过2000多B的状压我都不大会。。
设f[i][j][l]表示当前选到第i个,j是状态,表示i-k到i的状态,l表示有多少个三人桌。
那么明显,三人桌只能有3个,4个的话就可以被替代成为3个4人桌。
那么我们预处理val[i][j]表示i到i+k-1的状态为j的贡献,这个需要预处理一下。(lowbit(x)表示一个数最低的非0位)
再预处理一个trans[i,0/1,j]表示状态i放3/4人桌后可以转移到哪些状态。
那么就可以暴力枚举所有状态转移了。。
感觉好无脑的样子。。
看来还是我状压太弱了。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2e3+5;
typedef long long ll;
const ll inf=1e18;
int n,m,k;
int tot[N][2],trans[N][2][N],bin[20];
int a[N],bl[N],pre[N][N][5],tim;
ll f[N][N][5],val[N][N]; 
inline int lowbit(int x)
{
    return x&(-x);
}
inline ll calc(int i,int s)
{
    int cnt=0;
    ll tot=0;
    for(int tmp=s;tmp;tmp-=lowbit(tmp))
    {
        int x=lowbit(tmp);
        x=lower_bound(bin,bin+k,x)-bin+1;
        tot+=a[i+x-1];
        cnt++;
    }
    if (cnt!=3&&cnt!=4)return 0;
    tot=tot*12/cnt;
    ll ans=0;
    for(int tmp=s;tmp;tmp-=lowbit(tmp))
    {
        int x=lowbit(tmp);
        x=lower_bound(bin,bin+k,x)-bin+1;
        ans+=(a[i+x-1]*12-tot)*(a[i+x-1]*12-tot);
    }
    return ans;
}
inline void prework()
{
    fo(i,0,bin[k]-1)
    fo(j,0,bin[k]-1)
    {
        if((j|i)!=j)continue;
        int tmp=j^i,s=0;
        if (!(i&1)&&!(tmp&1))continue;
        while (tmp)s++,tmp-=lowbit(tmp);
        if (s==3)trans[i][0][++tot[i][0]]=j;
        else if (s==4)trans[i][1][++tot[i][1]]=j;
    }
    fo(i,1,n-k+1)
    fo(j,0,bin[k]-1)
    val[i][j]=calc(i,j);
}
inline void dp()
{
    fo(i,k,n)
    fo(j,0,bin[k]-1)
    fo(l,0,m)f[i][j][l]=inf;
    f[k][0][0]=0;
    fo(i,k,n)
    fo(j,0,bin[k]-1)
    fo(l,0,m)
    if (f[i][j][l]<inf)
    {   
        if (j&1)f[i+1][j>>1][l]=min(f[i+1][j>>1][l],f[i][j][l]);
        if (l<m)
        {
            fo(x,1,tot[j][0])
            if (f[i][j][l]+val[i-k+1][trans[j][0][x]^j]<f[i][trans[j][0][x]][l+1])
            {
                f[i][trans[j][0][x]][l+1]=f[i][j][l]+val[i-k+1][trans[j][0][x]^j];
                pre[i][trans[j][0][x]][l+1]=j+1;
            }
        }
        fo(x,1,tot[j][1])
        if (f[i][j][l]+val[i-k+1][trans[j][1][x]^j]<f[i][trans[j][1][x]][l])
        {
            f[i][trans[j][1][x]][l]=f[i][j][l]+val[i-k+1][trans[j][1][x]^j];
            pre[i][trans[j][1][x]][l]=j+1;
        }
    }
    printf("%lld\n",f[n][bin[k]-1][m]);
}
inline void solve(int i,int j,int l)
{
    if (i==k&&!j&&!l)return;
    if (!pre[i][j][l])solve(i-1,j*2+1,l);
    else
    {
        tim++;
        pre[i][j][l]--;
        int tmp=j^pre[i][j][l],cnt=0;
        for(;tmp;tmp-=lowbit(tmp))
        {
            int x=lowbit(tmp);
            x=lower_bound(bin,bin+k,x)-bin+1;
            bl[i-k+1+x-1]=tim;
            cnt++;
        }
        if (cnt==3)solve(i,pre[i][j][l],l-1);
        else solve(i,pre[i][j][l],l);
    }
}
int main()
{
    freopen("friends.in","r",stdin);
    freopen("friends.out","w",stdout);
    scanf("%d%d",&n,&k);k++;
    fo(i,1,n)scanf("%d",&a[i]);
    if (n%4==1)m=3;
    else if (n%4==2)m=2;
    else if (n%4==3)m=1;
    else m=0;
    bin[0]=1;
    fo(i,1,k)bin[i]=bin[i-1]*2;
    prework();dp();
    solve(n,bin[k]-1,m);
    fo(i,1,n)printf("%d ",bl[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值