zoukankan      html  css  js  c++  java
  • 1595:炮兵阵地

    1595:炮兵阵地

    时间限制: 1000 ms 内存限制: 524288 KB
    提交数: 503 通过数: 322
    【题目描述】
    原题来自:NOI 2001

    司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。一个 N×M 的地图由 N 行 M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

    如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

    现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

    【输入】
    第一行包含两个由空格分割开的正整数,分别表示 N 和 M;

    接下来的 N行,每一行含有连续的 M 个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。

    【输出】
    仅一行,包含一个整数 K,表示最多能摆放的炮兵部队的数量。

    【输入样例】
    5 4
    PHPP
    PPHH
    PPPP
    PHPP
    PHHP
    【输出样例】
    6
    【提示】
    数据范围与提示:

    N≤100,M≤10。

    思路:
    思路很简单吧。。(但是实现非常困难qwq,作为一个不怎么细心的孩子,调了三个小时qwq)
    设置变量为f[i][j][k]表示已经处理到了第i行,第i行状态是b[j],第i-1行状态是b[k]
    转移方程:f[i%3][k][j]=max(f[(i-1)%3][j][l]+sum[k],f[i%3][k][j]);
    注意,这里的i%3应用的是滚动数组的思想。因为对于一个状态来说,需要记录的只是与之相关的三行,其它的都可以由转移得到,所以为了减小空间复杂度,防止MLE,我们只记录三行

    做题时出问题的地方:
    1.dp的时候把对应的sum[k]加成了sum[j]
    2.f数组开小了,最后结果爆了
    3.初始化出锅

    代码(已AC):

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m;
    char a[1050][1050];
    int f[14][1050][1050];
    int b[1020000];
    int cnt;
    int sum[102000];
    int dat[102000];
    
    int count(int x)
    {
    	int num=0;
    	for(int i=0;i<m;i++)
    	{
    		if(x&(1<<i)) num++; 
    	}
    	return num;
    }
    
    void prepare()
    {
    	for(int i=0;i<(1<<m);i++)
    	{
    		if(!(i&(i<<1))&&!(i&(i<<2))) 
    		{
    			cnt++;
    			b[cnt]=i;
    			sum[cnt]=count(i);
    		}
    	}
    	
    //	for(int i=1;i<=cnt;i++) cout<<sum[i]<<" * ";
    //	cout<<'
    ';
    }
    
    void dp()
    {
    //	for(int i=1;i<=n;i++) cout<<dat[i]<<" ";
    //	cout<<'
    ';
    	/*cout<<"f[1][i][1]: 
    ";*/
    	for(int i=1;i<=cnt;i++)
    	{
    		if(!(b[i]&dat[1])) f[1][i][1]=sum[i]/*,cout<<f[1][i][1]<<" "*/;
    	}
    	/*cout<<'
    ';
    	int o=0;
    	cout<<"f[2][i][j]: 
    ";*/
    	int q_sum=0;
    	for(int i=1;i<=cnt;i++)
    	{
    		for(int j=1;j<=cnt;j++)
    		{
    //			cout<<i<<" "<<j<<" "<<b[i]&b[j]<<" "<<b[i]&dat[2]<<" ">>b[j]&
    			if(!(b[i]&b[j])&&!(b[i]&dat[2])&&!(b[j]&dat[1]))
    			{
    				f[2][i][j]=sum[i]+sum[j];/*cout<<f[2][i][j]<<" "<<b[i]<<" "<<b[j]<<'
    ';*/
    //				o++;
    				q_sum=(q_sum+f[2][i][j])%100000;
    			}
    		}
    		/*cout<<"ooooooooooooooooo "<<o<<'
    ';*/
    //		cout<<dat[1]<<" "<<dat[2]<<" dat
    ";
    		
    	}
    	/*cout<<'
    ';*/
    	int p=0;
    	
    	/*for(int k=1; k<=n; k++) {
    		for(int i=0; i<(1<<m); i++) {
    			for(int j=0; j<(1<<m); j++)
    			/*	cout<<f[k][i][j]<<" ";
    			cout<<'
    ';
    			}
    		cout<<'
    ';
    	}*/	
    	
    	/*for(int i=3; i<=n; i++)
    		for(int L=0; L<(1<<m); L++) { //i-1
    			if(L&b[i-1] || (L&(L<<1)) || (L&(L<<2))) continue;	//特判
    		int q_sum=0;	for(int S=0; S<(1<<m); S++) { //i
    				if(S&b[i] || L&S || (S&(S<<1)) || (S&(S<<2))) continue;
    				for(int FL=0; FL<(1<<m); FL++) { //i-2
    					if(FL&L || FL&S || FL&b[i-2] || (FL&(FL<<1)) || (FL&(FL<<2)))	continue;
    					f[L][S][i%3]=max(f[L][S][i%3],f[FL][L][(i-1)%3]+sum[S]),		//滚动数组的实现方法
    					p++;
    				}
    			}
    		}*/
    		
    	
    	for(int i=3;i<=n;i++)
    		for(int j=1;j<=cnt;j++)//第i-1行状态
    		{
    			if(!(b[j]&dat[i-1]))
    			for(int k=1;k<=cnt;k++)//i 
    			{
    				if(!(b[j]&b[k]))
    				for(int l=1;l<=cnt;l++)//i-2
    				{
    					if(!(b[j]&b[k])&&!(b[k]&b[l])&&!(b[l]&b[j])&&!(b[k]&dat[i])&&!(b[j]&dat[i-1])&&!(b[l]&dat[i-2])) 
    					{
    						f[i%3][k][j]=max(f[(i-1)%3][j][l]+sum[k],f[i%3][k][j]);
    //						q_sum+=f[(i-1)%3][j][l]+sum[k];
    //						q_sum%=1000000;
    						p++;
    					}
    				 } 
    			}
    		 } 
    		 
    //	cout<<p<<" pppppppppppppppp
    ";
    //	cout<<q_sum<<" summmmmmmmmmm
    ";
    		 
    	int ans=0;
    	for(int i=1;i<=cnt;i++)
    		for(int j=1;j<=cnt;j++)
    		{
    			ans=max(ans,f[n%3][i][j]);
    		}
    		
    	cout<<ans<<'
    ';
    	/*for(int k=1; k<=n; k++) {
    		for(int i=0; i<(1<<m); i++) {
    			for(int j=0; j<(1<<m); j++)
    				cout<<f[k][i][j]<<" ";
    			cout<<'
    ';
    			}
    		cout<<'
    ';
    	}	
    	*/
    }
    
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) cin>>a[i][j];
    		
    	for(int i=1;i<=n;i++)
    	{
    		int s=0;
    		for(int j=1;j<=m;j++)
    		{
    			if(a[i][j]=='P') 
    			{
    				s=(s<<1)+0;
    			}
    			else
    			{
    				s=(s<<1)+1;
    			}
    		}
    		dat[i]=s;
    	}
    	
    	/* for(int i=1;i<=n;i++) cout<<dat[i]<<" ";
        cout<<'
    ';
        cout<<"aghfukaku
    ";*/
    	
    	
    	prepare();
    	dp();
    	return 0;
    }
    

    ps:
    做题经验:
    1.平常做题的时候不要和题解对代码,不要跟着题解对拍,(特别是dp类题目,对拍根本拍不出来),最好是自己从头到尾看一遍自己的代码,检查一下是不是有什么细节问题
    2.跟着自己的感觉走,不要老是看题解,因为题解的方法可能很多,不一定是你的方法

    ヾ(◍°∇°◍)ノ゙

  • 相关阅读:
    复变函数
    abc136
    点集
    一些数学题
    牛客多校第六场
    牛客多校第五场G
    复数
    generator 1
    digits 2
    Winner
  • 原文地址:https://www.cnblogs.com/yxr001002/p/14429019.html
Copyright © 2011-2022 走看看