2018.01.02【CQOI2018】【BZOJ5299】【洛谷P4460】解锁屏幕(状压DP)(轻微卡常)

BZOJ传送门

洛谷传送门


解析:

熟悉的感觉。。。洛谷RK1,BZOJ RK2,前面的又是工程大佬xehoth。。。

思路:

这道题真的卡常,本来说用BFS可以优化一些不可能达到的状态,从而减少计算量。。。我还是太naive了。。。结果复杂度算下来 O ( 2 n × n 2 ) O(2^n\times n^2) O(2n×n2)。。。

这个。。真的能过?

于是BFS卡常也只卡出了80pts。。

然后转手去写朴素的状压了。。。

发现很多地方可以优化,一不小心就过了。。。

其实主要就是通过逻辑判断将运算引向不同分支,从而减少计算量。

当然,合理安排内存分配位置也是卡常必要的技巧。

这道题的状压倒是非常好想,所有初始状态方案数设置为1,然后记录状态为 f j , s t a f_{j,sta} fj,sta,表示当前已经经过了sta的点,在 j j j点上的状态数。

转移就是直接枚举补集中的每个元素,判断能否直接转移到就行了。
注意我们不可以越过还没有经过的点。

最后,这道题卡常的关键在于:减少函数的调用。
因为函数的调用本身需要从内存中分配栈空间,略微有点耗时间。
对于一些函数值,可以预处理在数组里面就预处理在数组里面吧。


代码(BFS):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define cs const

namespace IO{
	namespace IOONLY{
		cs int Rlen=1<<18|1;
		char buf[Rlen],*p1,*p2;
	}
	inline char get_char(){
		using namespace IOONLY;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re int num;
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

inline int max(cs int &x,cs int &y){return x>y?x:y;}
inline int min(cs int &x,cs int &y){return x<y?x:y;}

cs int mod=100000007;

inline void add(int &x,int y){
    x+=y;
    if(x>=mod)x-=mod;
}

cs int N=20;
struct state{
    int now,sta;
    state(){}
    state(cs int &_now,cs int &_state):now(_now),sta(_state){}
};
queue<state> q;

int x[N],y[N];
int sta[N][N];

inline bool onseg(int i,int j,int k){
    if(x[k]<min(x[i],x[j]) || x[k]>max(x[i],x[j]) || y[k]<min(y[i],y[j]) || y[k]>max(y[i],y[j]))return 0;
    return ((x[k]-x[i])*(y[j]-y[k])-(x[j]-x[k])*(y[k]-y[i])==0); 
}

inline bool enough(int sta){
    return __builtin_popcount(sta)>=4;
}

inline bool check(int i,int j,int state){
    return (sta[i][j]&state)==sta[i][j];
}

int f[N][1<<N];
bool inq[N][1<<N];
int n,ans;
signed main(){
    n=getint();
    for(int re i=0;i<n;++i){
        x[i]=getint();
        y[i]=getint();
    }
    for(int re i=0;i<n;++i){
        for(int re j=0;j<n;++j)
        if(i^j)for(int re k=0;k<n;++k){
            if((i^k)&&(j^k)&&onseg(i,j,k))sta[i][j]|=1<<k;
        }
    }
    for(int re i=0;i<n;++i)inq[i][1<<i]=true,f[i][1<<i]=1,q.push(state(i,1<<i));
    while(!q.empty()){
        re int u=q.front().now,sta=q.front().sta;q.pop();
        if(enough(sta))add(ans,f[u][sta]);
        for(int re i=0,j=1;i<n;++i,j<<=1)if(!(j&sta)){
            if(!check(u,i,sta))continue;
            if(!inq[i][sta|j])inq[i][sta|j]=true,q.push(state(i,sta|j));
            add(f[i][sta|j],f[u][sta]);
        }
    }
    printf("%d",ans);
    return 0;
}

代码(朴素状压):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const

namespace IO{
	namespace IOONLY{
		cs int Rlen=1<<15|1;
		char buf[Rlen],*p1,*p2;
	}
	inline char get_char(){
		using namespace IOONLY;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re int num;
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define lowbit(x) ((x)&-(x))

cs int N=20;
cs int mod=100000007;

int f[1<<N][N],sig[1<<N];
int sum[1<<N];
int x[N],y[N],sta[N][N];

signed main(){
	re int n=getint();
	for(int re i=0;i<n;++i)x[i]=getint(),y[i]=getint(),sig[1<<i]=i,f[1<<i][i]=1;
	for(int re i=0;i<n;++i){
		for(int re j=0;j<n;++j)if(i^j){
			re int dx=x[i]-x[j],dy=y[i]-y[j];
			re int l=min(x[i],x[j]),r=max(x[i],x[j]);
			re int u=max(y[i],y[j]),d=min(y[i],y[j]);
			for(int re k=0;k<n;++k){
				if(dx==0){
					if(x[k]^x[i])continue;
					if(y[k]<d||y[k]>u)continue;
					sta[i][j]|=1<<k;
				}
				else {
					if(x[k]<l||x[k]>r)continue;
					if(dx*(y[k]-y[i])==dy*(x[k]-x[i]))sta[i][j]|=1<<k;
				}
			}
		}
	}
	re int full=(1<<n)-1,ans=0;
	for(int re i=1,j=1;i<=full;j=++i)
	for(;j;j-=lowbit(j))++sum[i];
	for(int re i=1;i<=full;++i){
		for(int re j=0;j<n;++j){
			if(f[i][j]){
				f[i][j]%=mod;
				if(sum[i]>3){
					ans+=f[i][j];
					if(ans>=mod)ans-=mod;
				}
				re int comps=full^i;
				for(int re id=lowbit(comps);id;comps-=id,id=lowbit(comps)){
					if((full^i^id)&sta[j][sig[id]])continue;
					f[i|id][sig[id]]+=f[i][j];
				}
			}
		}
	}
	printf("%d",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值