Max Correct Set——简单状压DP

题目

CF1463F Max Correct Set

题解

一道简单的状压的题。容易发现,最优答案构成的数列选的位置一定是有循环节的,容易发现在贪心策略下,相隔x+y的两个位置的选取状态一定相同,因为对于a、a+x、a+y、a+x+y四个位置,若a处选入,那么a+x、a+y处必定没选,那么a+x+y处为何空着不选?肯定选了更优。若a处不选,那么a+x、a+y肯定有一处选入,那么a+x+y处也不能选。所以我们设一个状态长度22的状压DP,dp[i][j]表示在i位置,前max(x,y)个数的选取状态为j的最大集大小,dp的时候用个滚动,复杂度O((x+y)2^{22})

其实这个证明并不是很严谨,很多是靠打表硬证的,正如YYDSJZM说过

如果什么东西都要证明,那我们和数竞有什么区别

还有一个特殊处理的地方也是我打表发现的,就是当x+y除不尽n,余下的那一点的最优解可能和前面的循环节不一样,因为前面的证明是考虑前后互相影响下最终稳定的最优解,尾巴处自然要例外(这算是勉强证明吧

然后我发现纯贪心在小范围内是有效的,所以我们把循环节从优到劣排序,不超时的情况下(也就是掐表),每次贪心构造尾端的解,然后记录最优答案。

代码

跑得慢但是速度稳定

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define uns unsigned
#define MAXN 100005
#define INF 0x3f3f3f3f
#define QB 50000000
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int x,y,m,dp[2][4194310],sr[4194310];
ll n,ans;
inline bool cmp(int a,int b){
	return dp[(x+y)&1][a]>dp[(x+y)&1][b];
}
signed main()
{
	n=read(),x=read(),y=read();
	if(x>y)swap(x,y);m=(1<<y)-1;
	for(int i=1;i<=x+y;i++){
		for(int S=0,lim=(1<<min(i,y));S<lim;S++)dp[i&1][S]=0;
		for(int S=0,lim=(1<<min(i,y));S<lim;S++){
			dp[i&1][(S<<1)&m]=max(dp[i&1][(S<<1)&m],dp[~i&1][S]);
			if(((S>>(x-1))&1)==0&&((S>>(y-1))&1)==0)
				dp[i&1][(S<<1|1)&m]=max(dp[i&1][(S<<1|1)&m],dp[~i&1][S]+1);
		}
	}
	for(int S=0;S<=m;S++)sr[S]=S;
	sort(sr,sr+1+m,cmp);
	for(int i=0,lim=min(QB/(x+y),m);i<=lim;i++){
		ll res=dp[(x+y)&1][sr[i]]*(n/(x+y));
		int len=n%(x+y),S=sr[i];
		for(int j=1;j<=len;j++)
			if(((S>>(x-1))&1)==0&&((S>>(y-1))&1)==0)res++,S=(S<<1|1)&m;
			else S=(S<<1)&m;
		ans=max(ans,res);
	}
	printf("%lld\n",ans);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值