zoukankan      html  css  js  c++  java
  • [NOI2001]炮兵阵地

    容易想出状态 dp[t][i][j] ,表示当前在算第t行,这一行的状态为i,上一行的状态为j , 则状态转移方程就为 dp[t][i][j]=max{dp[t-1][j][k]}+pre[i] 其中k为上上行的状态,且i,j,k都满足题目要求,pre[i] 表示i在二进制下1的个数.

    首先考虑读入与预处理。因为在山地不能放炮兵,所以枚举状态时要判断能不能放,在dp过程中用的是二进制来表示状态,所以读入的时候也把每一行的情况转化成二进制。如果是H,则当前位为1,否则为0,例如PHPPH就表示为01001。又考虑到pre[i]要被多次调用,所以先预处理出来。

    预处理每行的状态(此处是从0到n-1)

    for(rint i=0;i<n;i++){
    	scanf("%s",s+1);
    	for(rint j=1;j<=m;j++){
    		a[i]<<=1;
    		a[i]|=(s[j]=='H');
    	}
    }
    

    预处理pre数组

    inline int Get(int i){
    	int sum=0;
    	while(i){sum+=i&1;i>>=1;}
    	return sum;
    }
    

    现在就来判断状态是否合法了

    先考虑每一行自身的合法性:

    1.炮兵不能放在山地上 , 所以如果 i&a[当前行数] 不为0就跳过。

    2.每个炮兵在横向至少隔两格(否则就会互相打到),即 i&(i<<1)||i&(i<<2) 为false。

    再考虑相邻的两行的合法性:

    3.不能有炮兵同时在一列上,即 i&j 为0

    最后考虑隔了一行的两行的合法性:

    4.与3相同,炮兵不能同时在一列上,即 i&k 为0

    考虑完后就可以开始状态转移了

    #define rint register int
    #define check(i,lin) (i&a[lin]||i&(i<<1)||i&(i<<2))
    
    for(rint t=2;t<n;t++)
    	for(rint i=0;i<p;i++){
    		if(check(i,t)) continue;
    		for(rint j=0;j<p;j++){
    			if(check(j,t-1)||(i&j)) continue;
    			for(rint k=0;k<p;k++){
    				if(check(k,t-2)||(i&k)||(k&j)) continue;
    				dp[t][i][j]=maxx(dp[t][i][j],dp[t-1][j][k]+pre[i]);
    			}
    		}
    	}
    

    然而分析一下空间复杂度$ 100*1024^2 $ ,愉快地炸掉了。再回去看代码,发现状态的第一维只与上一项有关,即t只和t-1有关,所以我们考虑压位,把第一维压掉。于是代码就变成了:

    for(rint t=2;t<n;t++)
            for(rint i=0;i<p;i++){
                if(check(i,t)) continue;
                for(rint j=0;j<p;j++){
                    if(check(j,t-1)||(i&j)) continue;
                    for(rint k=0;k<p;k++){
                        if(check(k,t-2)||(i&k)||(k&j)) continue;
                        dp[t&1][i][j]=maxx(dp[t&1][i][j],dp[(t-1)&1][j][k]+pre[i]);
                    }
                }
            }
    

    完整代码:

    #include<stdio.h>
    #define rint register int
    #define check(i,lin) (i&a[lin]||i&(i<<1)||i&(i<<2))
    #define N 103
    #define M 11
    
    int n,m,a[N],pre[1<<M],dp[2][1<<M][1<<M];
    char s[N];
    
    inline int Get(int i){
        int sum=0;
        while(i){sum+=i&1;i>>=1;}
        return sum;
    }
    inline int maxx(int x,int y){return x>y? x:y;}
    int main(){
        scanf("%d%d",&n,&m);
        int p=1<<m;
        for(rint i=0;i<n;i++){
            scanf("%s",s+1);
            for(rint j=1;j<=m;j++){
                a[i]<<=1;
                a[i]|=(s[j]=='H');
            }
        }
        for(rint i=0;i<p;i++) pre[i]=Get(i);
        if(n==1){
            int ans=0;
            for(rint i=0;i<p;i++)
                if(!check(i,0)) ans=maxx(ans,pre[i]);
            printf("%d",ans);
            return 0;
        }
        for(int i=0;i<p;i++){
            if(check(i,1)) continue;
            for(rint j=0;j<p;j++){
                if(check(j,0)||(i&j)) continue;
                dp[1][i][j]=pre[i]+pre[j];
            }
        }
        for(rint t=2;t<n;t++)
            for(rint i=0;i<p;i++){
                if(check(i,t)) continue;
                for(rint j=0;j<p;j++){
                    if(check(j,t-1)||(i&j)) continue;
                    for(rint k=0;k<p;k++){
                        if(check(k,t-2)||(i&k)||(k&j)) continue;
                        dp[t&1][i][j]=maxx(dp[t&1][i][j],dp[(t-1)&1][j][k]+pre[i]);
                    }
                }
            }
        int ans=0;
        for(rint i=0;i<p;i++)
            for(rint j=0;j<p;j++)
                ans=maxx(ans,dp[(n-1)&1][i][j]);
        printf("%d",ans);
    }
    
  • 相关阅读:
    [C语言]变长函数参数和变长参数宏
    [转载]腾讯机智团队分享--AllReduce算法的前世今生
    (三)opencv_py之阈值处理
    (二)opencv_py之彩色空间转换
    neo4j 一些常用的CQL
    Neo4j (1)创建节点
    tensorflow实现siamese网络 (附代码)
    早停!? earlystopping for keras
    关于 ESIM 网络的 资料 集合
    聊天机器人资源合集:项目,语聊,论文,教程
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/10846413.html
Copyright © 2011-2022 走看看