首页 > 其他分享 >dp交换值域定义域

dp交换值域定义域

时间:2022-11-18 09:33:52浏览次数:71  
标签:ch int 值域 rep read 定义域 s1 dp

前言

最近心血来潮学了一下这个套路?感觉很高级 qwq。

正文

我不好归纳,但他们大体都是一样的。

T1

[AGC033D] Complexity

题意

给定一个 \(N\) 行 \(M\) 列的字符矩阵。
我们定义一个字符矩阵的凌乱度为:
若这个字符矩阵中所有字符都相同,则凌乱度为 \(0\)。
否则,则考虑所有的沿水平或者竖直方向的直线,将字符矩阵分成两个不为空的部分,设两个部分的凌乱度分别为 \(a\) 和 \(b\),则整个字符矩阵的凌乱度为 \(\max(a,b)+1\) 的最小值。
请你求出,给出的字符矩阵的凌乱度是多少。
\(1 \leq N, M \leq 185\)。

Solution

我学习的是 Alex_wei 的博客。在这里自己阐述一遍加深理解qaq。

先考虑答案上限,就是全不相同,每次从中间切开,所以上界是 \(O(\log n + \log m)\) 的。

首先考虑朴素 \(dp\),形如 \(f[i][j][k][l]\) 为求出左上角 \((i,j)\) 右下角 \((k,l)\) 的答案。我们发现状态数就是 \(O(n^4)\) 的,转移复杂度很爆炸。

再进行一步观察发现一个显然的性质:

  • 矩形之间存在包含关系的时候,答案一定不降。

再深入一点:

  • 对于固定了左、上、下边界 \((i,j,k)\) 的矩形,随着右边界的增加答案不降。

于是考虑令 \(dp[i][j][k][x]\) 表示固定了左、上、下边界为 \((i,j,k)\) 的矩形,此时答案不超过 \(x\) 能到达的最右边界。

这实际上就是交换值域定义域了,非常巧妙,并且降低了复杂度,因为我们的答案是 \(O(\log)\) 级别的。

然后就是这个 \(x\) 因为每次最多加一,可以滚动数组,他没啥意义。

答案就是 \(dp[1][1][n] == m\) 的时候的 \(x\)。

下面的 \(f,g\) 是滚动数组。

转移分两种:

  • 横着切类似区间 \(dp\) 的转移

    \[f[i][j][k] = max(min(g[i][j][p],g[i][p + 1][k])) \]

  • 竖着切就类似倍增的转移

    \[f[i][j][k] = max(g[g[i][j][k] + 1][j][k]) \]

于是做完了 qaq 。

点击查看代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 190;
int n,m;
char s[N][N];
int f[N][N][N],ff[N][N][N];
int s1[N][N],s2[N][N];
inline int q1(int x1,int y1,int x2,int y2){
    return s1[x2][y2] + s1[x1 - 1][y1 - 1] - s1[x2][y1 - 1] - s1[x1 - 1][y2];
}
inline int q2(int x1,int y1,int x2,int y2){
    return s2[x2][y2] + s2[x1 - 1][y1 - 1] - s2[x2][y1 - 1] - s2[x1 - 1][y2];
}
signed main(){
    read(n,m);
    rep(i,1,n)scanf("%s",s[i] + 1);
    rep(i,1,n)rep(j,1,m){
        s1[i][j] = s1[i - 1][j] + s1[i][j - 1] - s1[i - 1][j - 1] + (s[i][j] == '#');
        s2[i][j] = s2[i - 1][j] + s2[i][j - 1] - s2[i - 1][j - 1] + (s[i][j] == '.');
    }
    rep(k,1,m){
        rep(l,1,n){
            int now = m;
            rep(r,l,n){
                while(q1(l,k,r,now) && q2(l,k,r,now))--now;
                f[k][l][r] = now;
            }
        }
    }
    for(int i = 0;; ++ i){
        if(f[1][1][n] == m){
            printf("%d\n",i);
            return 0;
        }
        rep(k,1,m){
            rep(l,1,n){
                int p = l;
                rep(r,l,n){
                    #define val(p) min(f[k][l][p],f[k][p + 1][r])
                    while(p + 1 < r && val(p) <= val(p + 1))++p;
                    ff[k][l][r] = max({val(p),f[k][l][r],f[f[k][l][r] + 1][l][r]});
                }
            }
        }
        swap(f,ff);
    }
}

T2

CF1342F

题意

长度为 \(n\) 的序列,\(n \le 15\) ,每次可以进行操作形如:选择 \(i,j\),令 \(a[j] += a[i]\) ,删除 \(a[i]\),求令序列严格递增的最小次数并输出方案。

Solution

神仙题!!!

题意等价于将序列划分成若干个集合,使得最后可以排成一个严格单增的序列。

那么考虑 \(f[i][j][k]\) 为考虑完前 \(i\) 个集合,第 \(i\) 个集合全部加在了 \(j\) 的身上,已经用过的集合为 \(k\) 时,第 \(i\) 个集合的最小值。

转移的时候直接枚举没选过的集合是什么,扔进第 \(i + 1\) 个集合里面,并且要保证扔在 \(p\) 右边的最左边的位置。

同理答案就是,从大往小枚举 \(i\) ,如果存在答案,就是他了!直接输出路径即可qaq。

点击查看代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 20;
int n,a[N];
const int M = (1 << 15) + 5;
int s[M],dp[N][N][M],id[N];
using pii = pair<int,int>;
struct qwq{
    int i,p,s;
};
#define endl putchar('\n')
#define spac putchar(' ')
template <typename T>
void write(T x){
    if(x >= 10)write(x / 10);
    putchar(x % 10 + '0');
}
qwq fa[N][N][M];
//dp[i][j][k]
//有i个集合,第i个都合并到j,已经选的集合为k,第i个集合最小值
const int INF = 0x3f3f3f3f;
#define pop(x) __builtin_ctz(x)
inline void ckmin(int &x,int y){if(x > y)x = y;}
inline void upd(qwq u,qwq v,int w){
    ckmin(dp[v.i][v.p][v.s],w);
    if(dp[v.i][v.p][v.s] == w)fa[v.i][v.p][v.s] = u;
}
inline void solve(){
    read(n);
    Rep(i,0,n)read(a[i]);
    Rep(st,0,1 << n){
        s[st] = 0;
        Rep(j,0,n)if(st >> j & 1)s[st] += a[j];
    }
    rep(i,0,n)rep(j,0,n)Rep(k,0,1 << n)dp[i][j][k] = INF;
    dp[0][0][0] = 0;
    Rep(i,0,n)Rep(j,0,n)Rep(k,0,1 << n)if(dp[i][j][k] != INF){
        qwq u = {i,j,k};
        int st = (((1 << n) - 1) ^ k);
        for(int s1 = st;s1;s1 = (s1 - 1) & st){
            if(s[s1] > dp[i][j][k] && (s1 >> j)){
                qwq v = {i + 1,j + 1 + pop(s1 >> j),k | s1};
                upd(u,v,s[s1]);
            }
        }
    }
    qwq ans = (qwq){-1,-1,-1};
    int to = (1 << n) - 1;
    rrep(i,n,1){
        rep(j,1,n)
            if(dp[i][j][to] != INF){
                ans = {i,j,to};
                break;
            }
        if(~ans.i)break;
    }
    // printf("%d\n",n - ans.i);
    write(n - ans.i);
    endl;
    Rep(i,0,n)id[i] = i + 1;
    while(ans.i != 0){
        qwq pre = fa[ans.i][ans.p][ans.s];
        int s0 = pre.s ^ ans.s;
        Rep(i,0,n){
            if((s0 >> i & 1) && (i != ans.p - 1)){
                // printf("%d %d\n",id[i],id[ans.p - 1]);
                write(id[i]);spac;write(id[ans.p - 1]);endl;
                Rep(j,i + 1,n)id[j]--;
            }
        }
        ans = pre;
    }
}
signed main(){
    int t;
    read(t);
    while(t--)solve();
}

标签:ch,int,值域,rep,read,定义域,s1,dp
From: https://www.cnblogs.com/wsxxs/p/16902121.html

相关文章

  • codeforces 2000左右dp题目练习
    栾巨的dp题单,刷完他要v我500可以提升dp水平。1.YaroslavandTwoStrings白给题,令\(f_{i,0/1,0/1}\)表示前\(i\)位,有无小于,有无大于,根据每位的字符转移即可。2.To......
  • dpm中的参数和颗粒数据读取
    rho0表示颗粒密度。sizeDistribution中的fixedvaludedistribution的value值表示颗粒直径(可以设置不同的直径分布函数和固定值)。cloudFunction中可以设置关于cloud的不同......
  • 【SSL 1590】旅游(线段树优化DP)
    旅游题目链接:SSL1590题目大意要从x号点依次按编号走到y号点,每次可以选择跳最多z个点,即从i到i+z。每到一个点都要支付a的费用,到一些给出的特定点有其对应的......
  • 闫式DP分析法
    闫式DP分析法闫式DP分析法的核心思想是从集合的角度来分析DP问题。所有的DP问题都可以使用闫式DP分析法进行分析。(经过70道题目验证通过)1.选择DP问题:背包模型2.序列DP......
  • python基础入门之黏包、UDP代码、多道技术、进程
    python基础入门之黏包、UDP代码、多道技术、进程目录python基础入门之黏包、UDP代码、多道技术、进程黏包现象黏包的解决方案UDP基本代码使用并发编程理论之操作系统发展......
  • UDP协议和实战、并发编程理论、多道技术、进程理论
    今日内容UDP协议和实战并发编程理论多道技术进程理论进程的并行与并发进程的三状态UDP协议#客户端importsocket#指定使用UDP协议,不指定的话......
  • 进入python的世界_day33_网络编程—— 黏包现象、UDP协议、并发编程理论
    一、黏包现象1.何为黏包​ 流逝协议:所有的数据类似于水流连接在一起的​ 数据量很小并且时间间隔很多那么就会自动组织到一起recv​ 我们不知道即将要接收的......
  • 单调队列优化DP
    先存这里理解了再继续编写CF1077F2PictureswithKittens(hardversion)#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constllN=5e3+10......
  • 黏包现象、struct模块和解决黏包问题的流程、UDP协议、并发编程理论、多道程序设计技
    目录黏包现象二、struct模块及解决黏包问题的流程三、粘包代码实战UDP协议(了解)并发编程理论多道技术进程理论进程的并行与并发进程的三状态黏包现象什么是粘包1.服务......
  • Java使用UDP协议进行通信的例子
    UDP是不可靠但是高效的一种传输协议,不管你接收端在不在,丢进去就行了。传输过程:发送端:/***发送端*1.使用DatagramSocket指定端口创建发送端*2.准备数据,转......