寒假训练——第三周(记忆化搜索)

A - Function Run Fun

A - Function Run Fun

思路:
-记忆化搜索

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 30, M = 1010;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int dp[N][N][N];

int w(int a, int b, int c)
{
    if(a <= 0 || b <= 0 || c <= 0) return 1;
    if(a > 20 || b > 20 || c > 20) return w(20, 20, 20);
    
    int &v = dp[a][b][c];
    
    if(v != -1) return v;
    else if(a < b && b < c) v =  w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
    else v = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);    
    
    return v;
}   

signed main()
{
    //fast;
    //int T = 1;
    //cin >> T;
    
    memset(dp, -1, sizeof dp);
    
    int a, b, c;
    while(cin >> a >> b >> c, ( (a != -1) || (b != -1) || (c != -1) ))
    // 也可如此特判:while(cin >> a >> b >> c, ~a || ~b || ~c)
        printf("w(%d, %d, %d) = %d\n", a, b, c, w(a, b, c));
    
    return 0;
}

B - 阿克曼函数

B - 阿克曼函数

思路:

  • 记忆化搜索

阿克曼函数:

简介: 可以在极短时间内将小数编程大数,因此记忆化搜索第二维 n n n 及其不好确认,因此先找规律,确认边界

阿克曼函数为 ( 2 , 2 ) (2, 2) (2,2) 时,举例:
在这里插入图片描述

规律如下:

int akm(int m,int n)
{
    if(m == 0) return n+1;
    if(m == 1) return n+2;
    if(m == 2) return 2*n+3;
    if(m == 3) return (1<<(n+3))-3;
}

可知递归边界的 n n n 最大为 1 << (n + 3)

记忆化搜索代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 5, M = 1 << 19;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;
int dp[N][M];

int akm(int m, int n)
{
    int &v = dp[m][n];
    
    if(v != -1) return v;
    else if(m == 0) v = n + 1;
    else if(m > 0 && n == 0) v = akm(m - 1, 1);
    else if(m > 0 && n > 0) v = akm(m - 1, akm(m, n - 1));  
    
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    cin >> m >> n;
    cout << akm(m, n) << endl;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

C - Free Candies

C - Free Candies

思路:

  • 记忆化搜索(直接搜会 T L E TLE TLE )

代码解释:

  • a[N][4]数组 表示 四个小桶里每个桶都有N个球
  • dp[N][N][N][N]表示当前每个桶装到了第几个球,数据内容为最大的对数
  • p[4]数组表示当前 第i个桶装到了第p[i]个球
  • st[N * 4]数组表示该球上的数字是否在桶里,分类讨论

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 60, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;
int p[4];
int a[N][4];
int dp[N][N][N][N];
bool st[N * 4];

int dfs(int cnt)
{
    int &v = dp[p[0]][p[1]][p[2]][p[3]];
    
    if(v != -1) return v;
    
    v = 0;
    if(cnt >= 5) return v;
    for (int i = 0; i < 4; i ++ )
    {
        if(p[i] >= n) continue;
        p[i] ++;
        
        if(st[a[p[i]][i]])
        {
            st[a[p[i]][i]] = false;
            v = max(v, dfs(cnt - 1) + 1);
            st[a[p[i]][i]] = true;
        }
        else
        {
            st[a[p[i]][i]] = true;
            v = max(v, dfs(cnt + 1));
            st[a[p[i]][i]] = false;
        }
        p[i] --;
    }
    
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    memset(st, 0, sizeof st);
    memset(p, 0, sizeof p);
    
    // 此处 i 从 1 开始,因为 p 数组初始化为 0 ,表示没有放入任何球
    // 若初始化 i 为 0 开始,则 p 数组初始化为 0,表示一开始就放入了 四个球
    for (int i = 1; i <= n; i ++ ) 
        for (int j = 0; j < 4; j ++ )
            cin >> a[i][j];
            
    cout << dfs(0) << endl;
    
    return;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(cin >> n, n)
        solve();
    
    return 0;
}

D - k-Tree

D - k-Tree

思路:

  • 直接搜,记忆化搜索

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 60, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, k, d;
int dp[110][2];

int dfs(int sum, bool flag)
{
    if(sum == 0)
    {
        if(flag) return 1;
        else return 0;
    }
    
    int &v = dp[sum][flag];
    if(v != -1) return v;
    
    v = 0;
    
    for (int i = 1; i <= k; i ++ )
    {
        if(sum >= i)
        {
            if(i >= d) flag = true;
            v += dfs(sum - i, flag);
            v %= mod;
        }
    }
    
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    
    cin >> n >> k >> d;
    
    cout << dfs(n, 0) << endl;
    // dfs(0, 0) 正搜,反搜均可
    
    return;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

浅浅改一下即可:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 60, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, k, d;
int dp[110][2];

int dfs(int sum, bool flag)
{
    if(sum > n) return 0;
    if(sum == n)
    {
        if(flag) return 1;
        else return 0;
    }
    
    int &v = dp[sum][flag];
    if(v != -1) return v;
    
    v = 0;
    
    for (int i = 1; i <= k; i ++ )
    {
        if(i >= d) flag = true;
        v += dfs(sum + i, flag);
        v %= mod;
    }
    
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    
    cin >> n >> k >> d;
    
    cout << dfs(0, 0) << endl;
    
    return;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

E - Cow Program

E - Cow Program

思路:

  • dp[i][j]数组,表示dp[i][j]的该情况下i需要加上的数
  • st[i][j]数组,表示 dp[i][j]是否用过
  • 数组第二维,0表示第二步,1表示第三步

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 2e6 + 10, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, k, d;
int a[N];
int dp[N][2];
bool st[N][2];

void dfs(int x, bool j)
{
    if (st[x][j]) return;
    
    st[x][j] = true;
    
    int y = x + a[x] * (j ? -1 : 1);
    
    if( y <= 0 || y > n)
    {
        dp[x][j] = a[x];
        return;
    }
    
    dfs(y, j ^ 1);
    
    if(dp[y][j ^ 1] != -1)
        dp[x][j] = dp[y][j ^ 1] + a[x];

    return;
}

void solve()
{
    memset(st, 0, sizeof st);
    memset(dp, -1, sizeof dp);
    
    cin >> n;
    
    for (int i = 2; i <= n; i ++ )
        cin >> a[i];
    
    st[1][0] = 1;
    st[1][1] = 1;
    dp[1][1] = 0;
    for (int i = 2; i <= n; i ++ )
        dfs(i, 1);
    
    for (int i = 2; i <= n; i ++ )
    {
        if(dp[i][1] != -1) dp[i][1] += i - 1;
        cout << dp[i][1] << endl;
    }
    
    return;
}

signed main()
{
    fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

F - Cheapest Palindrome

F - Cheapest Palindrome

思路:

  • 法一:区间 D P DP DP
  • 法二:记忆化搜索

区间 D P DP DP

状态表示:

  • f[i][j]表示将第i个字符到第j字符变为回文串的最小代价
  • 单字符f[i][i]和无字符f[i][i-1]本身就为回文串,最小代价为0

状态转移过程:

  1. s[i] == s[j]时,并且f[i+1][j-1]为回文串,此时最小代价为0,即f[i][j] = f[i+1][j-1]
  2. s[i] != s[j]时,此时分为四种情况 添头 添尾 删头 删尾
    如下所示:(w为执行此操作所需要的代价)
    • 添头:在i前添加一个字符s[j],此时f[i][j-1]为回文串,状态转移为f[i][j] = f[i][j-1] + w
    • 添尾:在j前添加一个字符s[i],此时f[i+1][j]为回文串,状态转移为f[i][j] = f[i+1][j] + w
    • 删头:删去i位置的字符s[i],此时f[i+1][j]为回文串,状态转移为f[i][j] = f[i+1][j] + w
    • 删尾:删去j位置的字符s[j],此时f[i][j-1]为回文串,状态转移为f[i][j] = f[i][j-1] + w
  3. 其中 添头删尾 所依托的状态相同,可合并计算;添尾删头 所依托的状态相同,可合并计算

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

const int N = 2e3 + 10, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;
char s[N];
int f[N][N];
map<char, int> cost;

void solve()
{
    memset(f, 0x3f, sizeof f);
    
    scanf("%d%d", &m, &n);
    scanf("%s", s + 1);
    char op[2];
    int x, y;
    for (int i = 1; i <= m; i ++ )
    {
        scanf("%s%lld%lld", op, &x, &y);
        cost[op[0]] = min(x, y);
    }
    
    for (int i = 1; i <= n; i ++ )
        f[i][i] = f[i][i - 1] = 0;
    
    for (int len = 2; len <= n; len ++ )
        for (int l = 1; l + len - 1 <= n; l ++ )
        {
            int r = l + len - 1;
            if(s[l] == s[r]) f[l][r] = f[l + 1][r - 1];
            f[l][r] = min(f[l][r], f[l + 1][r] + cost[s[l]]);
            f[l][r] = min(f[l][r], f[l][r - 1] + cost[s[r]]);
        }
    
    cout << f[1][n] << endl;
    
    return;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

记忆化搜索:

  • 状态转移和 区间 D P DP DP 差不多

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;

const int N = 2e3+10, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;
char s[N];
int dp[N][N];
map<char, int> cost;

int dfs(int l, int r)
{
    int &v = dp[l][r];
    
    if(v != -1) return v;
    
    if(l >= r) // 若:l + 1 == r && s[l] == s[r], 会出现 dfs(r, r - 1) 情况, 此时 l > r, 应该为 0
    {
        v = 0;
        return v;
    }
    
    if(s[l] == s[r]) v = dfs(l + 1, r - 1);
    else v = min(dfs(l + 1, r) + cost[s[l]], dfs(l, r - 1) + cost[s[r]]);
    
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    
    scanf("%lld %lld", &m, &n);
    scanf("%s", s + 1);
    char op[2];
    int x, y;
    for (int i = 1; i <= m; i ++ )
    {
        scanf("%s%lld%lld", op, &x, &y);
        cost[op[0]] = min(x, y);
    }
    
    cout << dfs(1, n) << endl;
    
    return;
}

signed main()
{
    //fast;
    int T = 1;
    //cin >> T;
    
    while(T -- )
        solve();
    
    return 0;
}

G - 不正经消消乐

G - 不正经消消乐

思路:

  • 记忆化搜索

具体步骤:

  • 先将所有颜色相同的连续的块合在一起,产生idx个大块,并保存其颜色(box[idx].color)和长度(box[idx].len)
  • 状态表示:
    1. 若:dp[i][j]:表示从大块 ij这一段消除后能得到的最高分,则无法将位于不同位置的相同颜色的大块合并成较高的分数,只能合并已有的分数,所以此状态表示不可以
    2. 若:dp[i][j][len]:表示j的右边已经有一个长度为len的大块,大块ij这一段和 len都消除后能得到的最高分,如此即可求出最大值,所求最大值即为dp[1][idx][0]
  • 状态计算:
    1. 若计算过则直接返回
    2. i==j只有一块了,直接计算,返回
    3. dp[i][j][len]=max(当前自己和之后合并, 和前面的大块合并并和之后的大块合并)

详细内容可参考此博客(写的很好哦):Bolcks

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>

#define fast ios::sync_with_stdio(false), cin.tie(nullptr); cout.tie(nullptr)

#define x first
#define y second
//#define int long long

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;

const int N = 210, M = 2e5 + 10;

const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int T, cases;
int n, m;
int dp[N][N][N];
map<char, int> cost;

struct Box
{
    int color, len;
}box[N];

int dfs(int i, int j, int len)
{
    int &v = dp[i][j][len];
    
    if(v != -1) return v;
    
    if(i == j)
    {
        v = (box[j].len + len) * (box[j].len + len);
        return v;
    }
    
    // 直接点,自己合并
    v = dfs(i, j - 1, 0) + (box[j].len + len) * (box[j].len + len);
    
    // 和前面的一起合并
    for (int k = i; k <= j - 1; k ++ )
        if(box[k].color == box[j].color)
            v = max(v, dfs(i, k, box[j].len + len) + dfs(k + 1, j - 1, 0));
            
    return v;
}

void solve()
{
    memset(dp, -1, sizeof dp);
    memset(box, 0, sizeof box);
    
    cin >> n;
    
    int x, t = -1, idx = 0;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> x;
        if(x != t)
        {
            idx ++;
            box[idx].len ++;
            box[idx].color = x;
            t = x;
        }
        else box[idx].len ++;
    }
    
    cout << "Case " << cases << ": " << dfs(1, idx, 0) << endl;
    
    return;
}

signed main()
{
    //fast;
    T = 1;
    cin >> T;
    
    for (cases = 1; cases <= T; cases ++)
        solve();
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AC自动寄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值