状压dp:方格取数(1),互不侵犯

方格取数(1):
题意给你 N ∗ N N*N NN矩阵,每一个位置有一个非负数吗,我们可以取任意多个数,但这些数不能相邻,问能取的最大数是多少? ( 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[i1][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 NN的矩阵,然后可以放 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<NN+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[i1][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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值