POJ 1185 炮兵陣地(動態規劃 狀態壓縮)

2021-09-06 11:41:12 字數 4734 閱讀 5867

炮兵陣地

description

司令部的將軍們打算在n*m的網格地圖上部署他們的炮兵部隊。乙個n*m的地圖由n行m列組成,地圖的每一格可能是山地(用"h" 表示),也可能是平原(用"p"表示),如下圖。在每一格平原地形上最多可以布置一支炮兵部隊(山地上不能夠部署炮兵部隊);一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示: 

如果在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中的黑色的網格表示它能夠攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。圖上其它白色網格均攻擊不到。從圖上可見炮兵的攻擊範圍不受地形的影響。 

現在,將軍們規劃如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其他支炮兵部隊的攻擊範圍內),在整個地圖區域內最多能夠擺放多少我軍的炮兵部隊。 

input

第一行包含兩個由空格分割開的正整數,分別表示n和m; 

接下來的n行,每一行含有連續的m個字元('p'或者'h'),中間沒有空格。按順序表示地圖中每一行的資料。n <= 100;m <= 10。

output

僅一行,包含乙個整數k,表示最多能擺放的炮兵部隊的數量。

sample input

5 4

phpp

pphh

pppp

phpp

phhp

sample output

6

分析:思路很簡單,因為每個位置要麼放一門炮、要麼不放,每行最多只有10個位置,所以可以用位儲存狀態,大體分如下兩步:

1.計算出每行所有的合法狀態,並用位表示。

2.dp~~~

f[p][i][j] = max    f[p][i][j]表示第p行取第i種方案,p-1行取第j種方案時的最大值,ct(p,i)表示第p行用第i種方案時用的炮的個數。

求整數x用二進位制表示時1的個數,只需要讓x不斷的執行x &= (x-1)就可以了,執行了多少次就有多少個1。

為什麼呢??可以分兩種情況,如果x最後一位是1,那麼執行一次與運算可以把這個1消掉,但是如果最後一位是0呢??這個時候消掉的是最靠右的乙個1,呵呵,可以簡單的畫一下看看。總之就是逐步消掉最後乙個1,消了幾次就有幾個1啦~~

第乙個**如下:

1 # include2 # include3 # include4

using

namespace

std;56

const

int n = 101;7

const

int m = 11;8

const

int inf = 0xffffff;9

10int

n,m;

11int ct[n][65]; //

ct[i][0]表示第i行放置炮的方案數,ct[i][j],j>0 表示第j種方案的狀態

12int tt[1030]; //

tt[i]表示 i 表示成2進製時的1的個數,即放置炮的數目

13int f[n][65][65]; //

f[p][i][j]表示第p行取第i種方案,p-1行取第j種方案時的最大值

14int hash[1030][1030

];15

char

map[n][m];

1617

int max(int x,int

y)20

21//

這一行的狀態

22void init_row(const

int &r,int x,int key)

28if(map[r][x] == '

p' && (key & 1)==0 &&(key & 2)==0) //

可以表示成狀態

29 init_row(r,x+1,(key<<1)+1

);30 init_row(r,x+1,key<<1

);31}32

33bool yes(int x,int y)

41 x>>=1

;42 y>>=1;43

}44if(hash[xx][yy] == -1) hash[xx][yy] = 1

;45 hash[yy][xx] =hash[xx][yy];

46return

hash[xx][yy];47}

4849

void

dp()61}

6263

for(p=3;p<=n;p++)75}

76}77}

78}7980

intmain()90}

91 scanf("

%d%d

",&n,&m);

92for(i=1;i<=n;i++)

93 scanf("%s"

,map[i]);

94for(i=1;i<=n;i++)

98dp();

99 ans = -inf;

100if(n==1

)101

for(i=1;i<=ct[n][0];i++)

102 ans = max(ans,f[n][i][0

]);103

else

108 printf("

%d\n

",ans);

109return0;

110 }

由於m很小,最多為10,所以可以對行進行狀態壓縮。二進位制對應位為1表示放炮兵,為0表示空。我們可以事先生成所有有效的狀態,即二進位制數任何兩個1都要相差兩位以上,同時用陣列記下此狀態有多少個炮兵。對於地形也進行狀態壓縮,用1表求高地,0表示平原。判斷某個狀態能否放到某個地形,就是地形狀態為1的地方,放置炮兵狀態一定為0,這點可以用位運算解決。判斷兩個狀態能否放在相鄰行與此相同。

**如下:

1 # include 2

using

namespace

std;34

const

int g = 70;5

const

int n =101;6

const

int m =11;7

8int d[2][g][g];//

滾動陣列

9int ph[n],f[g];//

ph陣列用於判斷狀態在第i行是否滿足在p的平原設定,f陣列則存放滿足的狀態

10int n,m,g;//

g為所有狀態數

1112

int onec(int x)

18return

t;19}20

void dp()33}

34}35}

36}37int

main()48}

49int v = 1

<

50for(i =0,g =0; i< v;i++)

54int

pmax ;

55if(n ==1

)61 printf("%d"

,pmax);

62return0;

63}64 memset(d, 0,sizeof

(d));

65 pmax = 0;66

for(i =0; i < g; i++)74}

75}76if(n ==2)80

dp();

81for(i =0; i < g; i ++)85}

86 printf("%d"

, pmax);

87return0;

88 }

**如下:

1 # include2 # include

3 # include4 # include5 # include6 # include7

using

namespace

std;

8int n,m,sum,num,sta[1

<<11],cot[1

<<11],dp[105][105][105],a[105];9

bool fit(int x,int

y)10

15void

init()

1629 cot[num++]=count;30}

31}32void

dp()

3343

for(int i=2;i<=n;i++)

44for(int j=0;j)

45for(int k=0;k)

4657

}58 printf("

%d\n

",ans);59}

60int

main()

6176}77

getchar();78}

79dp();

80return0;

81 }

另附轉的一篇

POJ 1185 炮兵陣地 動態規劃 狀態壓縮

由於遞推的時候依賴於三個連續層的關係.一開始想著直接三重for迴圈,但是這裡有個問題就是上一層的0位置上包括著上上層是0和1兩種可能,而後者又對當前行有約束,因此該方法不行.當然有乙個辦法就是增加狀態數,讓狀態能夠表示是從1還是從0轉移過來的.這題有個解法是採用多進製的狀態壓縮 網上瞄了別人的一眼解...

poj 1185 炮兵陣地

題目鏈結 題意 在n m的網格地圖上部署炮兵部隊。地圖的每一格可能是山地 用 h 表示 也可能是平原 用 p 表示 如下圖。在每一格平原地形上最多可以布置一支炮兵部隊 山地上不能夠部署炮兵部隊 一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示 如果在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中...

POJ 1185 炮兵陣地

include include include include include include include include include include include include include include define sz v int v size define rep i,n ...