Luogu P2704 [NOI2001] Artillery Position (State Compression DP+Optimization)

Keywords: PHP iOS

Topic Description
Command generals plan to deploy their artillery units on the NM grid map. A NM map consists of N rows and M columns. Each grid of the map may be mountain (represented by "H") or plain (represented by "P"), as shown below. At most one artillery unit can be deployed on each plain terrain (artillery units cannot be deployed on mountainous areas); the attack range of an artillery unit on the map is shown in the black area in the figure.

If an artillery unit is deployed on the gray-marked plain in the map, the black grid in the map represents the area it can attack: two squares left and right along the horizontal direction and two squares up and down along the vertical direction. No other white grids on the graph can be attacked. It can be seen from the map that the artillery's attack range is not affected by the terrain. Now, the generals plan how to deploy artillery units, on the premise of preventing accidental injuries (to ensure that no two artillery units can attack each other, that is, no artillery unit is within the scope of attack by other artillery units), and how many artillery units of our army can be placed in the whole map area at most.

Input and output format
Input format:
The first line contains two positive integers separated by spaces, representing N and M, respectively.

Next in line N, each line contains consecutive M characters ('P'or'H'), with no spaces in it. Represents the data of each row in the map in sequence. N < 100; M < 10.

Output format:
A single line, containing an integer K, indicates the maximum number of artillery units that can be placed.

Input and Output Samples
Input sample #1:
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Output sample #1:
6

Topic:

Ideas:

Pressure DP
Because each row has an impact on the following two rows, we define the DP state to maintain the information of the first two rows, that is, the DP state.
dp[i][j][k] represents line i, where the current line is the jth state, and the previous line is the kth state, with the largest number of deployments.

We know that if a row has m squares, it has at most 2^m states, and M has a maximum of 10.
So if we don't optimize the dp array to dp[100][1024][1024], we should know that this array is too big, we can't open such a large array.
So how should we optimize it? Let's start with a message in the title. Each row and one turret has an effect on both left and right positions. Subtracting one row, the distance between two 1 is at least 3, so that the number of legitimate States is very small.
Through the program, we can know that the number of legitimate States is not more than 110, so we can enumerate the first legitimate state to DP, and use an array INDEX to store the specific state information, that is, INDEX[i] is the specific state information of the second legitimate state.

In this way, we optimize time and space.

The equation of state transition is:
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][w]+num[INDEX[j]]);

See the code for details:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define rt return
#define dll(x) scanf("%I64d",&x)
#define xll(x) printf("%I64d\n",x)
#define sz(a) int(a.size())
#define all(a) a.begin(), a.end()
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll powmod(ll a,ll b,ll MOD){ll ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;}
inline void getInt(int* p);
const int maxn=1000010;
const int inf=0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n;
int m;
int a[500];
bool can[(1<<11)];
bool can2[300][123];
int dp[120][123][123];
int ans=0;
int INDEX[maxn];
int cnt=0;
int num[(1<<11)];
int main()
{
    // freopen("D:\\common_text\\code_stream\\in.txt","r",stdin);
    //freopen("D:\\common_text\code_stream\\out.txt","w",stdout);
    gbtb;
    cin>>n>>m;
    char c;
    repd(i,1,n)
    {
        repd(j,1,m)
        {
            cin>>c;
            if(c=='P')
            {
                a[i]=(a[i]<<1)+1;
            }else
            {
                a[i]=(a[i]<<1);
            }
        }
    }
    int maxstate=(1<<m)-1;// Maximum number of States
    for(int i=0;i<=maxstate;i++)
    {
        if(((i<<1)&i)==0&&((i<<2)&i)==0&&((i>>2)&i)==0&&((i>>1)&i)==0)// There should be at least two spaces between two 1's in the same line
        {
            INDEX[++cnt]=i;
            can[cnt]=1;
            int j=i;
            while(j)
            {
                if(j&1)
                {
                    num[i]++;
                }
                j>>=1;
            }
            
        }
    }
    // can2[i][j] represents the legal state in line I.
    for(int i=1;i<=cnt;i++)
    {
        int x=INDEX[i];
        if(((x&a[1])==x))
        {
            can2[1][i]=1;
            dp[1][i][0]=num[x];
        }
    }
    for(int i=1;i<=cnt;++i)
    {
        if(((INDEX[i]&a[2])==INDEX[i]))
        {
            can2[2][i]=1;
            for(int j=1;j<=cnt;++j)
            {
                if(can2[1][j])
                {
                    if((INDEX[i]&INDEX[j])==0)
                    {
                        dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num[INDEX[i]]);
                    }
                }
            }
        }
    }
    for(int i=3;i<=n;++i)
    {
        for(int j=1;j<=cnt;j++)
        {
            if(((INDEX[j]&a[i])==INDEX[j]))
            {
                can2[i][j]=1;
                for(int k=1;k<=cnt;k++)
                {
                    if(can2[i-1][k])
                    {
                        if((INDEX[k]&INDEX[j])==0)
                        {
                            for(int w=1;w<=cnt;++w)
                            {
                                if(can2[i-2][w])
                                {
                                    if((INDEX[w]&INDEX[j])==0&&(INDEX[w]&INDEX[k])==0)// 1 that does not coincide with either of the above lines
                                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][w]+num[INDEX[j]]);                                    
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<=cnt;++i)
    {
        for(int j=1;j<=cnt;j++)
        {
            ans=max(ans,dp[n][i][j]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

inline void getInt(int* p) {
    char ch;
    do {
        ch = getchar();
    } while (ch == ' ' || ch == '\n');
    if (ch == '-') {
        *p = -(getchar() - '0');
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 - ch + '0';
        }
    }
    else {
        *p = ch - '0';
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 + ch - '0';
        }
    }
}




Posted by cspgsl on Sun, 07 Jul 2019 13:51:07 -0700