zoukankan      html  css  js  c++  java
  • 【POJ1185】【洛谷P2704】炮兵阵地【状压dp】

    题目大意:

    题目链接:

    POJ:http://poj.org/problem?id=1185
    洛谷:https://www.luogu.org/problemnew/show/P2704

    在一个n×mn imes m的网格中,有些格子可以选择,任意两个选择的点不可以在同一直线的距离小于3。求最大的选择方案。


    思路:

    m10mleq 10,可以考虑状压dpdp
    由于对于一个点选择或者不选会影响到下面两行的选择,所以我们可以设f[i][j][k]f[i][j][k]表示第ii行,这一行和上一行的放置方案为jjkk的方案数。设上上行的状态为ll,则有
    f[i][j][k]=max(f[i][j][k],f[i1][k][l]+sum[j])f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+sum[j])
    其中sum[j]sum[j]表示状态为jj放置的个数(即状态为jj中数字1的个数)
    但是这样的时间复杂度为O(n×(2m)3)O(n imes (2^m)^3),空间复杂度为O(nm2)O(nm^2),TLE+MLE两开花(TM两开花XD)。
    我们会发现枚举时有太多状态是不成立的。也就是说这个状态中就有两个1之间的距离小于3,。所以我们可以预处理出所有的合法的状态(即任意两个1之间的距离至少为3),然后直接枚举这些状态(顺便预处理出sumsum数组)。
    这样可以省去超级多的无用状态,大大减少时间复杂度。
    然后我们为了减少空间,可以设f[i][j][k]f[i][j][k]表示第ii行,这一行的状态是第j个合法的状态,上一行的状态是第k个合法的状态时的最多放置方案数。方程没有怎么变。
    这样就可以过掉本题了。


    代码:

    
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    const int N=110;
    const int MAXN=(1<<10);
    int n,m,maxn,map[N],q[MAXN],sum[MAXN],tot,ans;
    char ch;
    
    bool check(int x)  //判断这个状态是否合法
    {
    	int cnt=0,sum;
    	while (x)
    	{
    		if ((x&1)&&cnt) return 0;
    		if (x&1) cnt=3;
    		if (cnt) cnt--;
    		x>>=1;
    	}
    	return 1;
    }
    
    int count(int x)  //计算这个撞他中有几个1(您强可以使用lowbit)
    {
    	int cnt=0;
    	while (x)
    	{
    		cnt+=(x&1);
    		x>>=1;
    	}
    	return cnt;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    		{
    			cin>>ch;
    			map[i]=(map[i]<<1)+(ch=='H');  //压缩这一行的那些位置可以放
    		}
    	maxn=(1<<m);
    	for (int S=0;S<maxn;S++)  //预处理
    		if (check(S)) 
    		{
    			q[++tot]=S;
    			sum[tot]=count(S);
    		}
    	int f[N][tot+1][tot+1];  //压缩空间复杂度
    	memset(f,0,sizeof(f));
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=tot;j++)
    			if (i==1||!(q[j]&map[i-2]))
    				for (int k=1;k<=tot;k++)
    					if (!(q[k]&map[i-1])&&!(q[j]&q[k]))
    						for (int l=1;l<=tot;l++)
    							if (!(q[l]&map[i])&&!(q[j]&q[l])&&!(q[k]&q[l]))
    								f[i][l][k]=max(f[i][l][k],f[i-1][k][j]+sum[l]);
    	for (int i=1;i<=tot;i++)
    		for (int j=1;j<=tot;j++)
    			ans=max(ans,f[n][i][j]);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    关于lockkeyword
    关于多层for循环迭代的效率优化问题
    Android 面试精华题目总结
    Linux基础回想(1)——Linux系统概述
    linux源代码编译安装OpenCV
    校赛热身 Problem C. Sometimes Naive (状压dp)
    校赛热身 Problem C. Sometimes Naive (状压dp)
    校赛热身 Problem B. Matrix Fast Power
    校赛热身 Problem B. Matrix Fast Power
    集合的划分(递推)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998274.html
Copyright © 2011-2022 走看看