# 状态压缩DP

POJ 1185 炮兵阵地

POJ 1321 棋盘问题

POJ 2411 Mondriaan's Dream

POJ 3254 Corn Fields

POJ 2441 Arrange the Bulls

HDU - 1565 方格取数(1)

# 二，OJ实战

## POJ 1185 炮兵阵地

Description

Input

Output

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

bool ok(int n)		//保证每2个1之间至少有2个0
{
int a = 3, b = 5;
for (int i = 0; i < 10; i++)
{
if ((n & a) == a)return false;
if ((n & b) == b)return false;
a *= 2;
b *= 2;
}
return true;
}



#include<iostream>
#include<string.h>
using namespace std;

long long sum[2][1024][1024];
int list[100], a, b, n, m, f[1024];
char c;
int ok[60] = { 0, 1, 2, 4, 8, 9, 16, 17, 18, 32, 33, 34, 36, 64, 65, 66, 68, 72, 73, 128, 129, 130, 132,
136, 137, 144, 145, 146, 256, 257, 258, 260, 264, 265, 272, 273, 274, 288, 289, 290, 292,
512, 513, 514, 516, 520, 521, 528, 529, 530, 544, 545,546,548,576,577,578,580,584, 585 };
int r1, r2, r3;

int main()
{
cin >> n >> m;
memset(list, 0, sizeof(list));
memset(sum, 0, sizeof(sum));
memset(f, 0, sizeof(f));
for (int i = 0; i < 1024; i++)for (int j = 0; j < m; j++)if (i & (1 << j))f[i]++;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> c;
if (c == 'P')list[i] += (1 << j);
}
}
long long s = 0;
if (n == 1)
{
for (int j = 0; j < 60; j++)
if ((ok[j] & list[0]) == ok[j] && s < f[ok[j]])s = f[ok[j]];
cout << s;
return 0;
}
for (int i = 0; i < n - 1; i++)	//第i行和第i+1行
{
a = i % 2, b = 1 - a;
for (int j = 0; j < 60; j++)		//第i行
{
r2 = ok[j];
if ((r2 & list[i]) != r2)continue;
for (int k = 0; k < 60; k++)		//第i+1行
{
r3 = ok[k];
if ((r3 & list[i + 1]) != r3 || (r3 & r2) != 0)continue;
sum[b][r2][r3] = f[r3];
if (i == 0) {
sum[b][r2][r3] += f[r2];
continue;
}
for (int l = 0; l < 60; l++)	//第i-1行
{
r1 = ok[l];
if ((r1 & list[i - 1]) == r1 && (r1 & r2) == 0 && (r1 & r3) == 0 && sum[b][r2][r3] < sum[a][r1][r2] + f[r3])
{
sum[b][r2][r3] = sum[a][r1][r2] + f[r3];
}
}
}
}
}
for (int j = 0; j < 60; j++)for (int k = 0; k < 60; k++)
if (s < sum[b][ok[j]][ok[k]])s = sum[b][ok[j]][ok[k]];
cout << s;
return 0;
}

## POJ 1321 棋盘问题

Description

Input

Output

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1

#include<iostream>
#include<string.h>
using namespace std;

int list[8];		//输入的棋盘状态
char c;
int sum;
int result;		//最后的结果

int main()
{
int n, k;
while (cin >> n >> k)
{
if (n == -1)break;
memset(list, 0, sizeof(list));
result = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> c;
if (c == '#')list[i] += (1 << j);
}
}
for (int i7 = 0; i7 < (1 << n); i7++)//8层循环，要注意顺序，因为如果n<7的话i7只能是0
{
if (i7 != (i7&(-i7)))continue;
if ((i7&list[7]) != i7)continue;
for (int i6 = 0; i6 < (1 << n); i6++)
{
if (i6 != (i6&(-i6)))continue;	//1行只能放1个
if ((i6&list[6]) != i6)continue;	//只能放在写了'#'的格子里面
if (i6&i7)continue;		//1列只能放1个
for (int i5 = 0; i5 < (1 << n); i5++)
{
if (i5 != (i5&(-i5)))continue;
if ((i5&list[5]) != i5)continue;
if (i5&(i6 | i7))continue;
for (int i4 = 0; i4 < (1 << n); i4++)
{
if (i4 != (i4&(-i4)))continue;
if ((i4&list[4]) != i4)continue;
if (i4&(i5 | i6 | i7))continue;
for (int i3 = 0; i3 < (1 << n); i3++)
{
if (i3 != (i3&(-i3)))continue;
if ((i3&list[3]) != i3)continue;
if (i3&(i4 | i5 | i6 | i7))continue;
for (int i2 = 0; i2 < (1 << n); i2++)
{
if (i2 != (i2&(-i2)))continue;
if ((i2&list[2]) != i2)continue;
if (i2&(i3 | i4 | i5 | i6 | i7))continue;
for (int i1 = 0; i1 < (1 << n); i1++)
{
if (i1 != (i1&(-i1)))continue;
if ((i1&list[1]) != i1)continue;
if (i1&(i2 | i3 | i4 | i5 | i6 | i7))continue;
for (int i0 = 0; i0 < (1 << n); i0++)
{
if (i0 != (i0&(-i0)))continue;
if ((i0&list[0]) != i0)continue;
if (i0&(i1 | i2 | i3 | i4 | i5 | i6 | i7))continue;
sum = 0;
if (i0)sum++;			//整个棋盘放了多少个棋子
if (i1)sum++;
if (i2)sum++;
if (i3)sum++;
if (i4)sum++;
if (i5)sum++;
if (i6)sum++;
if (i7)sum++;
if (sum == k)result++;
}
}
}
}
}
}
}
}
cout << result << endl
}
return 0;
}

（为了把这个代码弄的这么好看，我也是拼了。。。）

#include<iostream>
#include<string.h>
using namespace std;

int list[8];		//输入的棋盘状态
int r[8][256];
char c;
int sum;
int k;

bool ok(int n)
{
int s = 0;
for (int a = 1; a < 256; a *= 2)if (n & a)s++;
if (s>k)return false;
return true;
}

int main()
{
int n;
while (cin >> n >> k)
{
if (n == -1)break;
memset(list, 0, sizeof(list));
memset(r, 0, sizeof(r));
for (int i = 0; i < n; i++)		//输入
{
for (int j = 0; j < n; j++)
{
cin >> c;
if (c == '#')list[i] += (1 << j);
}
}
for (int i = 0; i < (1 << n); i++)
if ((i&list[0]) == i && ok(i)&&(i&(-i))==i)r[0][i] = 1;
for (int i = 1; i < n; i++)for (int j = 0; j < (1 << n); j++)
if (ok(j))for (int kk = 0; kk < (1 << n); kk++)
if ((j&kk) == kk && ((j^kk)&list[i]) == (j^kk) && ((j^kk)&(-(j^kk))) == (j^kk))
r[i][j] += r[i - 1][kk];
sum = 0;
for (int i = 0; i < (1 << n); i++)
{
int s = 0;
for (int a = 1; a < 256; a *= 2)if (i & a)s++;
if(s==k)sum += r[n - 1][i];
}
cout << sum << endl;
}
return 0;
}

r[i][j] 表示的是前i行的状态是 j 的情况数。

## POJ 2411 Mondriaan's Dream

Description

Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!

Input

The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.

Output

For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.

Sample Input

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0


Sample Output

1
0
1
2
3
5
144
51205


#include<iostream>
#include<string.h>
using namespace std;

int h, w;
long long dp[11][2048];	//前i行的和
long long sum;

//对于每一行，横为0，竖为1，所以00011是不可能的，00100是可能的
bool ok(int n)		//每2个0都是在一起的
{
n += (1 << w);	//因为不知道维度的奇偶性
while (n)
{
if (n % 2)n /= 2;
else
{
if (n % 4)return false;
n /= 4;
}
}
return true;
}

int main()
{
while (cin >> h >> w)
{
if (h == 0)break;
if (h == 1)
{
cout << (w + 1) % 2 << endl;
continue;
}
if (h % 2 && w % 2)
{
cout << 0 << endl;
continue;
}
memset(dp, 0, sizeof(dp));
for (int i = 0; i < (1 << w); i++)if(ok(i))dp[0][i] = 1;
for (int i = 1; i < h-1; i++)
{
for (int j = 0; j < (1 << w); j++)
{
for (int k = 0; k < (1 << w); k++)
{
if (j&k)continue;
if (ok(j^k))
{
dp[i][j] += dp[i - 1][k];
}
}

}
}
sum = 0;
for (int k = 0; k < (1 << w); k++)if (ok(k))sum += dp[h - 2][k];
cout << sum << endl;
}
return 0;
}

## POJ 3254 Corn Fields

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers:  M and  N
Lines 2..  M+1: Line  i+1 describes row  i of the pasture with  N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

#include<iostream>
#include<string.h>
using namespace std;

int list[13];
int r[13][4096];

bool ok(int n)		//一行里面不能有相邻的2个
{
for (int i = 3; i <= 3072; i *= 2)if ((n&i) == i)return false;
return true;
}

int main()
{
int m, n;
int a;
cin >> m >> n;
memset(list, 0, sizeof(list));
for (int i = 1; i <= m; i++)		//输入
{
for (int j = 0; j < n; j++)
{
cin >> a;
if (a)list[i] += (1 << j);
}
}
memset(r, 0, sizeof(r));
for (int i = 1; i <= m; i++)for (int j = 0; j < (1 << n); j++)		//求解
if (ok(j) && (j&list[i]) == j)		//单就这一行来说是满足的
{
if (i == 1)r[1][j] = 1;
else for (int k = 0; k < (1 << n); k++)
if ((k&j) == 0)r[i][j] = (r[i - 1][k] + r[i][j]) % 100000000;
}
int s = 0;
for (int i = 0; i < (1 << n); i++)s = (s + r[m][i]) % 100000000;	//对最后一行求和
cout << s;
return 0;
}

## POJ 2441 Arrange the Bulls

Description

Farmer Johnson's Bulls love playing basketball very much. But none of them would like to play basketball with the other bulls because they believe that the others are all very weak. Farmer Johnson has N cows (we number the cows from 1 to N) and M barns (we number the barns from 1 to M), which is his bulls' basketball fields. However, his bulls are all very captious, they only like to play in some specific barns, and don’t want to share a barn with the others.
So it is difficult for Farmer Johnson to arrange his bulls, he wants you to help him. Of course, find one solution is easy, but your task is to find how many solutions there are.
You should know that a solution is a situation that every bull can play basketball in a barn he likes and no two bulls share a barn.
To make the problem a little easy, it is assumed that the number of solutions will not exceed 10000000.

Input

In the first line of input contains two integers N and M (1 <= N <= 20, 1 <= M <= 20). Then come N lines. The i-th line first contains an integer P (1 <= P <= M) referring to the number of barns cow i likes to play in. Then follow P integers, which give the number of there P barns.

Output

Print a single integer in a line, which is the number of solutions.

Sample Input

3 4
2 1 4
2 1 3
2 2 4

Sample Output

4

#include<iostream>
#include<string.h>
using namespace std;

int list[20];	//每一行输入的数据
int r[20][1048576];
int n, m;

int f(int num)		//求num一共有多少位是1
{
int sum = 0;
for (int i = 1; i < (1 << m); i *= 2)if (num&i)sum++;
return sum;
}

int main()
{
cin >> n >> m;
int a, b;
memset(list, 0, sizeof(list));
for (int i = 0; i < n; i++)
{
cin >> a;
for (int j = 0; j < a; j++)
{
cin >> b;
list[i] += (1 << (b-1));
}
}
for (int i = 1; i < (1 << m); i++)if ((i&(-i))==i && (i&list[0]) == i)r[0][i] = 1;
int k;
for (int i = 1; i < n; i++)for (int j = 1; j < (1 << m); j++)
if (f(j) == i + 1)for (int t = 1; t < (1 << m); t *= 2)
if ((t&list[i]) == t && (t&j) == t)r[i][j] += r[i - 1][t^j];
int sum = 0;
for (int i = 0; i < (1 << m); i++)sum += r[n - 1][i];
cout << sum;
return 0;
}

#include<iostream>
#include<string.h>
#include<queue>
using namespace std;

int list[20];	//每一行输入的数据
queue<int>q;
int r[1048576];
int n, m;

int main()
{
cin >> n >> m;
int a, b;
memset(list, 0, sizeof(list));
memset(r, 0, sizeof(r));
for (int i = 0; i < n; i++)
{
cin >> a;
for (int j = 0; j < a; j++)
{
cin >> b;
list[i] += (1 << (b - 1));
}
}
int i, j, t;
q.push(0);
r[0] = 1;
int sum = 0;
while (!q.empty())
{
i = q.front();
q.pop();
j = 0;
for (int ii = 1; ii < (1 << m); ii *= 2)if (i&ii)j++;
if (j == n)sum += r[i];
else
{
for (t = 1; t < (1 << m); t *= 2)
{
if (i&t)continue;
if ((t&list[j]) == t)
{
if (r[t | i] == 0) q.push(t | i);
r[t | i] += r[i];
}
}
}
}
cout << sum;
return 0;
}



（顺便一提，函数 f 仍然用了，只不过写到main函数里面去了，没有写成函数的形式）

## HDU - 1565 方格取数(1)

Input

Output

Sample Input

3
75 15 21
75 15 28
34 70 5 

Sample Output

188


#include<iostream>
using namespace std;

int n, num[20000], s = 1, maxx[20][20000], number[20];

void build(int k)
{
if (k >20)return;
for (int i = 0; num[i] < 1 << (k - 2); i++)num[++s] = num[i] + (1 << (k - 1));
build(k + 1);
}

int getsum(int j)
{
int sum = 0;
for (int i = 0; i < n; i++)sum += ((j&(1 << i))>0)*number[i];
return sum;
}

int main()
{
num[0] = 0, num[1] = 1;
build(2);
while (cin >> n)
{
if (n == 0)
{
cout << "0\n";
continue;
}
for (int i = 0; i < n; i++)for (int j = 0; num[j] < (1 << n); j++)maxx[i][j] = 0;
for (int i = 0; i < n; i++)cin >> number[i];
for (int j = 0; num[j] < (1 << n); j++)maxx[0][j] = getsum(num[j]);
for (int i = 1; i < n; i++)
{
for (int i = 0; i < n; i++)cin >> number[i];
for (int j = 0; num[j] < (1 << n); j++)for (int k = 0; num[k] < (1 << n); k++)
if ((num[j] & num[k]) == 0 && maxx[i][j] < maxx[i - 1][k] + getsum(num[j]))
maxx[i][j] = maxx[i - 1][k] + getsum(num[j]);
}
int ans = 0;
for (int j = 0; num[j] < (1 << n); j++)if (ans < maxx[n - 1][j])ans = maxx[n - 1][j];
cout << ans << endl;
}
return 0;
}

07-16 1万+

02-04 3万+
02-16 885
11-11 138
04-28 699
05-31 1517
08-22 1866
05-25 2526
08-27 569
01-09 1796
07-15 3万+
06-28 524
07-17 175
09-04 9196
03-19 4861
04-07 739
08-02 846
08-05 37
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客