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;
}
posted @ 2018-06-08 21:33  skl_win  阅读(289)  评论(0编辑  收藏  举报
Live2D