bzoj5369: [PKUSC2018]最大前缀和 (状压dp)

www.cnblogs.com/shaokele/


bzoj5369: [PKUSC2018]最大前缀和

  Time Limit: 20 Sec
  Memory Limit: 512 MB
  p1
 
  p2
  

题目地址:  bzoj5369: [PKUSC2018]最大前缀和

题目大意:   全排列,求每次最大前缀之和

题解:

  考试时没A 哭唧唧
  
  其实就是一个状态压缩啦
  
  具体看代码
  


AC代码

#include <cstdio> 
using namespace std;
const int N=21,mo=998244353;
int n;
int a[N],f[1<<N],g[1<<N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    //f[S] 表示取S的状态使前缀>0的方案数 
    //g[S] 表示取S的状态使前缀<=0的方案数
    for(int i=1;i<(1<<n);i++){
        int now=0,s=0;
        for(int j=1;j<=n;j++)
            if(i&(1<<(j-1)))
                now+=a[j],s++;
        if(s==1)f[i]=1;
        else
            for(int j=1;j<=n;j++)
                if((i&(1<<(j-1))) && now-a[j]>0)
                    f[i]=(f[i]+f[i^(1<<(j-1))])%mo;    //当前i的情况可以从f[i^(1<<(j-1))]推过来 
    }
    g[0]=1;
    for(int i=1;i<(1<<n);i++){
        int now=0,s=0;
        for(int j=1;j<=n;j++)
            if(i&(1<<(j-1)))
                now+=a[j],s++;
        if(now>0)g[i]=0;
        else{
            if(s==1){
                g[i]=1;
                continue; 
            } 
            for(int j=1;j<=n;j++)
                if(i&(1<<(j-1)))
                    g[i]=(g[i]+g[i^(1<<(j-1))])%mo;   //同 f 
        }
    }
    int ans=0;
    for(int i=1;i<(1<<n);i++){ 
        int now=0,del=(1ll*f[i]*g[(1<<n)-1-i])%mo;   //now:值  del:方案数 
        for(int j=1;j<=n;j++)
            if(i&(1<<(j-1)))now=(now+a[j]+mo)%mo;
        now=(1ll*now*del)%mo;
        ans=(ans+now)%mo;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/shaokele/p/9157644.html
今日推荐