题目大意:有一个n*m的草地(草地上有的是沼泽),现在要分配牛去上面吃草,要求每头牛不能相邻(不能有公共边),问有多少种分配方案,一头牛都不分配也算一种分配方案
解题思路:这是碰到的第一道比较另类的压缩
1.首先考虑一下,每一行该怎么分配牛才不会让他们相邻,可以用状态压缩,0表示不放牛,1表示放牛,枚举一下有多少种可行的方案并纪录下来
2.接着考虑一下,因为不能相邻,而相邻的行之间又会相互影响。
考虑到第一行是比较特殊的,可以先枚举第一行的解决方案,接下来再枚举其他行。
在枚举其他行的情况时,要考虑和上一行的方案是否能共存,再考虑一下该方案是否可行(有沼泽影响)
如何判断该方案是否可行,可以先预处理一下地图,将地图也压缩成一个二进制数,0表示草地,1表示沼泽,然后将要枚举的方案和该行状态进行与运算,如果相与结果不为0,表示有牛放在了沼泽地,那么方案就不可行了
判断和上一行能否共存的判断和上面判断差不多,用该行的方案和上一行方案进行与运算,如果结果不为0,表示有两头牛放在了同一列,那么方案不可行
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 20
#define maxm 5010
#define mod 100000000
int R, C;
int row[maxn], state[maxm], dp[maxn][maxm];
int cnt;
void init() {
memset(row, 0, sizeof(row));
memset(dp, 0, sizeof(dp));
int t;
//处理地图,1表示沼泽
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++) {
scanf("%d", &t);
if(!t)
row[i] |= (1 << j);
}
cnt = 0;
//枚举一下符合的状态,如果i和(i << 1)的与结果不为0,表示有相邻了
for (int i = 0; i < (1 << C); i++) {
if (i & (i << 1))
continue;
state[cnt++] = i;
}
//枚举第一行,
for (int i = 0; i < cnt; i++) {
if (state[i] & row[0])
continue;
dp[0][i] = 1;
}
for (int r = 1; r < R; r++)//枚举第r行
for (int i = 0; i < cnt; i++) {//枚举第r行的方案
if (state[i] & row[r]) //判断该行是否支持该方案
continue;
for (int j = 0; j < cnt; j++) {//枚举第r-1行的方案
if (state[j] & row[r-1])
continue;
if (state[i] & state[j])//判断行之间知否有相邻的牛
continue;
dp[r][i] = (dp[r-1][j] + dp[r][i]) % mod;
}
}
}
int solve() {
int ans = 0;
for (int i = 0; i < cnt; i++)
ans = (ans + dp[R-1][i]) % mod;
return ans;
}
int main() {
scanf("%d%d", &R, &C);
init();
printf("%d\n", solve());
return 0;
}
这题和上题差不多,只不过是多了一个判断
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 110
#define maxm 20
#define maxs 70
int R, C, cnt;
char str[15];
int row[maxn], num[maxn], statu[maxn];
int dp[maxn][maxs][maxs];
void input() {
memset(row, 0, sizeof(row));
for(int i = 0; i < R; i++) {
scanf("%s", str);
for(int j = 0; j < C; j++)
if(str[j] == ‘H‘)
row[i] += (1 << j);
}
}
void init_statu() {
memset(num, 0, sizeof(num));
cnt = 0;
for(int i = 0; i < (1 << C); i++) {
if((i & (i << 1)) || (i & (i << 2)))
continue;
int t = i;
while(t) {
num[cnt] += (t & 1);
t >>= 1;
}
statu[cnt++] = i;
}
}
void init_DP() {
memset(dp, 0, sizeof(dp));
for(int i = 0; i < cnt; i++) {
if(statu[i] & row[0])
continue;
dp[0][i][0] = num[i];
}
for(int i = 0; i < cnt; i++) {
if(statu[i] & row[1])
continue;
for(int j = 0; j < cnt; j++) {
if(statu[j] & row[0])
continue;
if(statu[i] & statu[j])
continue;
dp[1][i][j] = max(dp[1][i][j], dp[0][j][0] + num[i]);
}
}
}
int solve() {
for(int r = 2; r < R; r++) {
for(int i = 0; i < cnt; i++) {
if(statu[i] & row[r])
continue;
for(int j = 0; j < cnt; j++) {
if(statu[j] & row[r - 1])
continue;
if(statu[i] & statu[j])
continue;
for(int k = 0; k < cnt; k++) {
if(statu[k] & statu[j])
continue;
if(statu[k] & statu[i])
continue;
if(statu[k] & row[r - 2])
continue;
dp[r][i][j] = max(dp[r][i][j], dp[r - 1][j][k] + num[i]);
}
}
}
}
int ans = 0;
for(int i = 0; i < cnt; i++)
for(int j = 0; j < cnt; j++)
ans = max(ans, dp[R-1][i][j]);
return ans;
}
int main() {
while(scanf("%d%d", &R, &C) != EOF) {
input();
init_statu();
init_DP();
printf("%d\n", solve());
}
return 0;
}
题目大意:有一个人要送披萨到N个地方,同一个地方可以去多次,问送完所有披赛再回到店里走的最短路径是多少
解题思路:可以将所有的地方压缩成一个状态,0表示还没有经过,1表示已经经过了,然后再bfs就可以求得结果了
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 15
#define maxn 1050
#define INF 0x3f3f3f3f
using namespace std;
struct DP{
int num, state;
}start;
int n, g[N][N];
int dp[N][maxn];
void init(){
int t;
for(int i = 0; i < n; i++)
for(int j = 0; j < n ; j++) {
scanf("%d", &t);
g[i][j] = t;
}
memset(dp, 0x3f, sizeof(dp));
start.num = 0;
start.state = 1;
dp[0][1] = 0;
}
int solve() {
queue<DP> q;
q.push(start);
while(!q.empty()) {
DP t = q.front();
q.pop();
for(int i = 0; i < n; i++) {
if(i == t.num)
continue;
if(dp[i][t.state | (1 << i)] > dp[t.num][t.state] + g[t.num][i]) {
dp[i][t.state | (1 << i)] = dp[t.num][t.state] + g[t.num][i];
DP tt;
tt.num = i;
tt.state = (t.state) | (1 << i);
q.push(tt);
}
}
}
return dp[0][(1 << n) - 1];
}
int main() {
while(scanf("%d", &n) != EOF && n) {
n++;
init();
printf("%d\n", solve());
}
return 0;
}
题目大意:有n个原子,两个原子相互碰撞的话,就会产生能量,并且另一个原子会消失,问n个原子能产生的最大能量是多少
解题思路:注意这题,能量有可能是负的,所以不需要每个原子都用掉。
用0表示没用,1表示用了,进行压缩
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 15
#define maxn 1200
int power[N][N];
int dp[maxn];
int n;
int main() {
while(scanf("%d", &n) != EOF && n) {
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
scanf("%d", &power[i][j]);
memset(dp, 0, sizeof(dp));
for(int i = 0; i < (1 << n); i++)
for(int j = 0; j < n; j++) {
if((i & (1 << j)))
continue;
for(int k = 0; k < n; k++) {
if(!(i & (1 << k)) && k != j) {
dp[i | (1 << j)] = max(dp[i | (1 << j)], dp[i] + power[k][j]);
}
}
}
int ans = 0;
for(int i = 0; i < (1 << n); i++)
ans = max(ans, dp[i]);
printf("%d\n", ans);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-01 12:36:04