方格取数(1):
题意给你
N
∗
N
N*N
N∗N矩阵,每一个位置有一个非负数吗,我们可以取任意多个数,但这些数不能相邻,问能取的最大数是多少?
(
0
<
N
<
21
)
(0<N<21)
(0<N<21)
思路:
理解炮兵阵地后,这个题就变的十分简单了,我们可以先处理出长度为
n
n
n的合理状态,并求出这个合理状态的累加和,然后初始化第一行
d
p
[
1
]
[
i
]
dp[1][i]
dp[1][i]代表第一层所有状态的答案,然后就从第二层开始进行状态的枚举即可,特别判断一下
s
t
k
[
j
]
stk[j]
stk[j]&
s
t
k
[
x
]
stk[x]
stk[x],代表代表当前层的状态,和上一层的状态,两个状态不能有
1
1
1重叠,然后进行
d
p
dp
dp即可,状态转移方程很好推:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
x
]
+
n
p
[
i
]
[
j
]
)
dp[i][j]=max(dp[i][j],dp[i-1][x]+np[i][j])
dp[i][j]=max(dp[i][j],dp[i−1][x]+np[i][j])
参考代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <math.h>
using namespace std;
typedef long long ll;
//#define ll long long
const ll N = 2e3 + 20;
const int maxn = 1e5 + 20;
const ll mod = 1000000007;
ll inv[maxn], vis[maxn], dis[maxn], head[maxn], dep[maxn], out[maxn];
ll fac[maxn], a[maxn], b[maxn], c[maxn], pre[maxn], cnt, sizx[maxn];
vector<ll> vec;
char s[maxn];
//typedef pair<ll, ll> p;
//priority_queue<p, vector<p>, greater<p> > m;
ll sum[maxn];
ll max(ll a, ll b) { return a > b ? a : b; }
ll min(ll a, ll b) { return a < b ? a : b; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { return a * b / gcd(a, b); }
void swap(int &x, int &y) { x ^= y, y ^= x, x ^= y; }
map<ll, ll> mp;
ll ksm(ll a, ll b)
{
a %= mod;
ll ans = 1ll;
while (b)
{
if (b & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1ll;
}
return ans;
}
ll lowbit(ll x)
{
return x & (-x);
}
int dp[25][1<<17], stk[maxn], w[25][25], np[25][1<<17];
int getsum(int x, int k, int n)
{
int ans = 0;
for (int i = 0; i < n; i++)
{
if ((x >> i) & 1)
ans += w[k][i + 1];
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t, n, m, k;
// cin>>t;
// while(t--)
// {
//}
while (cin >> n)
{
memset(dp, 0, sizeof dp);
memset(np, 0, sizeof np);
memset(stk, 0, sizeof stk);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> w[i][j];
}
}
k = 1;
for (int i = 0; i < (1 << n); i++)
{
if (!(i & (i << 1)))
stk[k++] = i;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j < k; j++)
{
np[i][j] = getsum(stk[j], i, n);
//cout << np[i][j] << ' ';
}
//cout << endl;
}
for (int i = 1; i < k; i++)
dp[1][i] = np[1][i];
for (int i = 2; i <= n; i++)
{
for (int j = 1; j < k; j++)
{
for (int x = 1; x < k; x++)
{
if (stk[j] & stk[x])
continue;
dp[i][j] = max(dp[i][j], dp[i - 1][x] + np[i][j]);
}
}
}
//cout << "******" << endl;
ll ans = 0;
for (int j = 1; j < k; j++)
{
ans = max(ans, dp[n][j]);
}
//cout << endl;
cout << ans << endl;
}
}
互不侵犯:
题意:
给你
N
∗
N
N*N
N∗N的矩阵,然后可以放
k
k
k个国王,国王不能攻击自己的上下左右,坐上右上左下右下八个方向,国王之间不能互相攻击,求摆放
k
k
k个国王的最大方案数?
(
0
<
N
<
10
,
−
1
<
k
<
N
∗
N
+
1
)
(0<N<10,-1<k<N*N+1)
(0<N<10,−1<k<N∗N+1)
思路:
状态压缩+完全背包
这题也十分简单,首先一样的套路,处理出每一行的可行状态,求出每一行可以放多少国王,然后预处理第一层,然后开始
d
p
dp
dp,
d
p
dp
dp开个三维的,
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k],代表第
i
i
i层
j
j
j状态放了
k
k
k个国王,在
f
o
r
for
for循环的时候,因为国王攻击八个方向,所以特判当前层和上一层的关系,若
(
s
t
k
[
j
]
(stk[j]
(stk[j]&
s
t
k
[
x
]
)
∣
∣
(
(
s
t
k
[
j
]
<
<
1
)
stk[x]) || ((stk[j] << 1)
stk[x])∣∣((stk[j]<<1)&
s
t
k
[
x
]
)
∣
∣
(
(
s
t
k
[
j
]
>
>
1
)
stk[x]) || ((stk[j] >> 1)
stk[x])∣∣((stk[j]>>1)&
s
t
k
[
x
]
)
)
stk[x]))
stk[x]))成立,说明当前层在上一层的攻击范围内,反则不在,动态转移方程也特别容易推:
d
p
[
i
]
[
j
]
[
n
p
[
j
]
+
y
]
+
=
d
p
[
i
−
1
]
[
x
]
[
y
]
dp[i][j][np[j] + y] += dp[i - 1][x][y]
dp[i][j][np[j]+y]+=dp[i−1][x][y],最后累加第
n
n
n层所有放了
k
k
k个国王的状态即是答案。
参考代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <math.h>
using namespace std;
typedef long long ll;
//#define ll long long
const ll N = 2e3 + 20;
const int maxn = 5e5 + 20;
const ll mod = 1000000007;
ll inv[maxn], vis[maxn], dis[maxn], head[maxn], dep[maxn], out[maxn];
ll fac[maxn], a[maxn], b[maxn], c[maxn], pre[maxn], cnt, sizx[maxn];
vector<ll> vec;
char s[maxn];
//typedef pair<ll, ll> p;
//priority_queue<p, vector<p>, greater<p> > m;
ll sum[maxn];
ll max(ll a, ll b) { return a > b ? a : b; }
ll min(ll a, ll b) { return a < b ? a : b; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { return a * b / gcd(a, b); }
void swap(int &x, int &y) { x ^= y, y ^= x, x ^= y; }
map<ll, ll> mp;
ll ksm(ll a, ll b)
{
a %= mod;
ll ans = 1ll;
while (b)
{
if (b & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1ll;
}
return ans;
}
ll lowbit(ll x)
{
return x & (-x);
}
//int dx[105], dp[105], nc[105], w[105];
ll dp[15][1025][105];
int stk[N], np[N], sp[105];
int getsum(int x)
{
int res = 0;
while (x)
{
if (x & 1)
res++;
x >>= 1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t, n, m, k, s;
// cin>>t;
// while(t--)
// {
cin >> n >> m;
// }
k = 1;
for (int i = 0; i < (1 << n); i++)
{
if (!(i & (i << 1)))
stk[k] = i, np[k++] = getsum(i);
}
for (int i = 1; i < k; i++)
dp[1][i][np[i]] = 1;
for (int i = 2; i <= n; i++)
{
for (int j = 1; j < k; j++)
{
for (int x = 1; x < k; x++)
{
if ((stk[j] & stk[x]) || ((stk[j] << 1) & stk[x]) || ((stk[j] >> 1) & stk[x]))
continue;
for (int y = 0; y <= m; y++)
{
dp[i][j][np[j] + y] += dp[i - 1][x][y];
}
}
}
}
ll ans = 0;
for (int i = 1; i < k; i++)
ans += dp[n][i][m];
cout << ans << endl;
}