思路:

直接说思路吧,毕竟中文题目。

先说说正解:

状压dp:令dp[i][j][k],表示当前枚举到第i 行,第i行的状态为j,前一行的状态为k,所得到最大的炮车数量!

内存的优化:

分析一下知道,m = 10,状态就有1 << 10  = 1000种,这样开出来的数组 是 dp[100][1024][1024],太浪费内存!

因此我们可以先预处理状态来优化!这个题目两个炮车之间的空位置必须大于2,因此那些任意两个1之间的空位置小于等于2的,我们都不要了,这样存到一个数组mb[]里,m=10的话 跑一跑程序知道,最多60种!

因此内存我们可以优化到:dp[100][60][60],轻松搞定!

转移思路:

在说说转移的方式:

我们直接枚举每一行 ,先预处理第一行,直接算出每一个状态有几个1即可!

然后枚举下面的,在单独处理第二行!其余的行,就统一处理了,枚举当前行i的状态,和i-1行的状态,i-2行的状态(如果有i-2行的话),然后看看这些状态冲突不冲突,(这些都是位运算了,仔细考虑后 还是比较简单的!)

最后在统计最后一行的最多数量即可了!

=======

在说一说非正解思路:

这个思路只是想到了而已,简单记录下吧:

最大团算法,这个算法也是比较适合解决 放最多棋子的问题!

我们直接给每一个位置进行标号!

然后任意两个号如果不能相互攻击到的话,就给他们连一条线!

然后最后求这个图的最大团即可!

用最大团方法需要注意:

可能有的模板  最大团最小值是1,但这个题目 最小值可以是0!注意一下即可!

不过 勉强过去了!

限时2s的题 ,这个方法 竟然 1954ms 过了 真是给跪了!! 也是可能这个题数据比较水!

=======

详细见代码:(状压dp)


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = 100 + 10;
char s[maxn][17];
int dp[maxn][67][67];
int number[maxn];
int mb[maxn],cnt;
int n,m;
stack<int>bug;
int getnum(int i){
    int ret = 0;
    while(i){
        i &= i-1;
        ++ret;
    }
    return ret;
}
//void debug(int i){
//    while(i){
//        bug.push(i%2);
//        i/=2;
//    }
//    while(!bug.empty()){
//        printf("%d",bug.top());
//        bug.pop();
//    }
//    puts("");
//}
void init(){
    cnt = 0;
    for (int i = 0; i < (1<<m); ++i){
        if ( (i & (i << 1)) ||  (i & (i << 2)) )continue;
        mb[cnt++] = i;
    }
}
bool check(int i,int j){
    int t = i | (~j);
    return getnum(~t) == 0;
}
int main(){
//    init();
    while(scanf("%d %d",&n, &m) == 2){
        memset(dp,-1,sizeof dp);
        for (int i = 1; i <= n; ++i){
            scanf("%s",s[i] + 1);
        }
        for (int i = 1; i <= n; ++i){
            int v = 0;
            for (int j = 1; j <= m; ++j){
                v = v*2 + (s[i][j] == 'P');
            }
            number[i] = v;
        }
        init();
        for (int i = 0; i < cnt; ++i){
            if (check(number[1],mb[i])){
                dp[1][i][0] = max(dp[1][i][0],getnum(mb[i]));
            }
        }
        for (int i = 2; i <= n; ++i){
            for (int j = 0; j < cnt; ++j){ // the i'th row;
                if (!check(number[i],mb[j]))continue;
                for (int k = 0; k < cnt; ++k){ // the i-1'th row;
                    if (!check(number[i-1],mb[k]))continue;
                    if (mb[k] & mb[j])continue;
                    if (i == 2){
                        dp[i][j][k] = max(getnum(mb[j] + mb[k]),dp[i][j][k]);
                    }
                    else {
                        for (int l = 0; l < cnt; ++l){ // the i-2'th row;
                            if (!check(number[i-2],mb[l]))continue;
                            if (mb[k] & mb[l])continue;
                            if (mb[j] & mb[l])continue;
                            if (dp[i-1][k][l] == -1)continue;
                            dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][l] + getnum(mb[j]));
                        }
                    }
                }
            }
        }
        int ans = 0;
        if (n == 1){
            for (int i = 0; i < cnt; ++i){
                ans = max(ans,dp[1][i][0]);
            }
        }
        else {
            for (int i = 0; i < cnt; ++i){ // the n'th row;
                for (int j = 0; j < cnt; ++j){ // the n-1'th row;
                    if (!check(number[n],mb[i]) || !check(number[n-1],mb[j]))continue;
                    if (mb[i] & mb[j])continue;
                    ans = max(ans,dp[n][i ][j ]);
                }
            }
        }
        printf("%d\n",ans);

    }

    return 0;
}




最大团思路代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000 + 100;
char s[107][17];
int g[1007][1007];
int mc[1007];
int len[1007];
int ans;
bool found;
int n;
int List[maxn][maxn];
void dfs(int Size){
    int i, j, k;
    if (len[Size] == 0){
        if (Size > ans){
            ans = Size;
            found = true;
        }
        return ;
    }
    for (k = 0; k<len[Size] && !found; ++k){
        if (Size + len[Size]-k <= ans)
            break;
        i = List[Size][k];
        if (Size+mc[i] <= ans)
            break;
        for (j = k+1,len[Size+1] = 0; j < len[Size]; ++j)
            if (g[i][List[Size][j] ])
                List[Size+1][len[Size+1]++ ] = List[Size][j];
        dfs(Size+1);
    }
}
void max_cluster(){
    int i,j;
    mc[n] = ans = 1;
    for (i=  n-1; i; --i){
        found = false;
        len[1] = 0;
        for (j = i+1; j <= n; ++j)
            if (g[i][j])
                List[1][len[1]++ ] = j;
        dfs(1);
        mc[i] = ans;
    }
}
int r,c;
void init(){
    memset(g,0,sizeof g);
    memset(mc,0,sizeof mc);
    memset(len,0,sizeof len);
    memset(List,0,sizeof List);
    for (int i = 1; i <= r; ++i){
        for (int j = 1; j <= c; ++j){
            if (s[i][j] == 'H')continue;
            for (int k = 1; k <= r; ++k){
                for (int l = 1; l <= c; ++l){
                    if (s[k][l] == 'H' || (k == i && l == j))continue;
                    bool ok = 1;
                    if (k == i){
                        if (abs(j-l) <= 2)ok=0;
                    }
                    else if (j == l){
                        if (abs(i-k) <= 2)ok=0;
                    }
                    if (ok){
                        g[(i-1)*c + j][(k-1)*c+l ] = 1;
//                        printf("%d - %d\n",(i-1)*c + j,(k-1)*c+l);
                    }
                }
            }

        }


    }

}
int main(){
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    while(scanf("%d %d",&r,&c) == 2){
        n = (r-1)*c+c;
        for (int i = 1; i <= r; ++i){
            scanf("%s",s[i]+1);
        }
        int sum = 0;
        for (int i =1; i <= r; ++i){
            for (int j = 1; j <= c; ++j){
                sum += (s[i][j] == 'P');


            }

        }
        if (!sum){
            printf("0\n");
            continue;
        }
        init();
        max_cluster();
        printf("%d\n",ans);
    }
    return 0;
}






炮兵阵地



Description



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



POJ 1185 炮兵阵地 (状压DP  ||  最大团算法)_算法


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


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



Input


第一行包含两个由空格分割开的正整数,分别表示N和M; 
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。


Output


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


Sample Input


5 4PHPPPPHHPPPPPHPPPHHP


Sample Output


6


Source


Noi 01

Time Limit: 2000MS

 

Memory Limit: 65536K

Total Submissions: 26348

 

Accepted: 10157


[Submit]   [Go Back]   [Status]   [Discuss]