• poj 1185 炮兵阵地 三进制状态压缩,DFS,滚动数组


      我们把 放置在 第 I 行的炮兵 称为 I 行炮兵, 通过观察我们可以得出, 第I行炮兵只受  I-1 行,I-2行的 放置情况影响.

      对于第Y列,有三种情况:

         P[x] = 0,    i-1 行 空  i-2 行 空    

         P[x] = 1,    i-1 行 空, i-2 行放置了炮兵  

         P[x] = 2,    i-1 行 放置炮兵    (因为 此行放置了炮兵, 哪怕 I-2行 空,对于 I行而言,也毫无意义)

      因为这题与 1038不同, 若当前位置为平原, 只影响当前层,而对下一层无影响,所以我们不能将 平原和高山 情况合并到状态中, 其实直接单点考虑也没事.

      因为一共M列,每一列都由一个 三进制数表示, 则(I-1,I-2)的放置情况可以通过 一串三进制的序列表示,为 P1,P2,...,Pm,称为放置序列

      

      我们可以通过 (I,I-1)的放置情况得出 I+1 层放置方案, 而且要更新得到 (I+1,I)放置序列

      其实去掉 I-1 层的影响就可以了, 这也是为何 将 I行 放置炮兵时,P[x] = 2, 因为其还能向下影响 两行,随着行数增多,影响逐渐变小

      (I+1,I)层 放置序列 Q[y] =  P[y] > 0 ? P[y]-1 : 0 

      定义状态 dp(I,J),表示 第I行, 状态J 表示  (I,I-1)层的放置情况 下最大 炮兵部署数量

      方程转移有三种情况:

        一,当前层不放置任何炮兵,此时 dp(i+1,k) = dp(i,j)

        二,对于当前列 y, 可以不放置,考虑y+1开始放置

        三,若 P[y] = 0, 则放置炮兵并设定状态 k = k + 2*3^(y-1) , 继续考虑 y+3列

    解题代码

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #define MAX(a,b) (a)>(b)?(a):(b)
    
    int dp[2][60000], A[11], P[11], Q[11];
    char mp[110][15];
    int n, m, mask;
    
    int GetState(int f[])
    {
        int k = 0;    
        for(int i = 1; i <= m; i++)
            k += f[i]*A[i-1];
        return k;
    }
    int GetBack( int x, int f[] )
    {
        for(int i = 1; i <= m; i++)
        {    f[i] = x%3; x /= 3; }
    }
    void input()
    {
        A[0] = 1;
        for(int i = 1; i <= 10; i++)
            A[i] = 3*A[i-1];
        
        for(int i = 1; i <= n; i++)
            scanf("%s", mp[i]+1 );
    }
    
    void dfs( int i, int x, int num )
    {
        if( x > m ) return;
        int cur = (i+1)&1, nxt = i&1, k = GetState(Q);
        //当前层 可以不放置炮兵
        dp[nxt][k] = MAX( dp[nxt][k], dp[cur][GetState(P)] );
    
        //情况一,当前列x不放,考虑x+1列
        if( x < m ) dfs( i, x+1, num );
        //情况二,若当前位置可以放置,且(i+1,x)位置为平原
        if( (mp[i][x]=='P') && (P[x]==0) && (Q[x]==0))
        {
        //    printf("x = %d,y = %d, ch = %c\n", i+1,x,mp[i+1][x]);    
            Q[x] = 2;
            int kk = GetState(Q);
            dp[nxt][kk] = MAX( dp[nxt][kk], num+1 );
            dfs( i, x+3, num+1 );
            Q[x] = 0;
        }
    
    }
    void solve()
    {
        memset( P, 0, sizeof(P) );
        memset( dp, 0xff, sizeof(dp) );
        dp[0][0] = 0;
    
        mask = A[m];
        for(int i = 1; i <= n; i++)
        {
            int cur = (i+1)&1, nxt = i&1;
            memset( dp[nxt], 0xff, sizeof(dp[nxt]) );    
            for(int j = 0; j < mask; j++)
            {
                if( dp[cur][j] == -1 ) continue;
                
                GetBack( j, P );
                for(int x = 1; x <= m; x++)
                    Q[x] =( (P[x] > 0) ? (P[x]-1) : 0 );
                dfs( i, 1, dp[cur][j] );
            }
        }
        int ans = 0;
        for(int i = 0; i < mask; i++)
            ans = MAX( ans, MAX( dp[0][i],dp[1][i] ) );
        printf("%d\n", ans );
    }
    
    int main()
    {
        while( scanf("%d%d",&n,&m) != EOF)
        {
            input();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    [LeetCode]Sliding Window Maximum
    判断两根线段是否相交
    求幂,我居然又没做出来
    C++集合运算函数总结 & 需要有序集合的操作
    effective stl读书笔记 & stl里面提供的算法 & emplace & ostream_iterator
    利用位操作的几道题目
    C++的new_handler
    TCP的可靠性 窗口滑动 拥塞控制
    关于高性能网络编程的一些知识
    三种连接 & DOS & SYNFLOOD & 防御
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2862871.html
Copyright © 2020-2023  润新知