2024.7.1杂谈(数位dp,状压,匈牙利,网络流)
数位dp
通常是“统计 \([L,R]\) 中满足某种特征的数的个数”的形式,用前缀和思想拆成两部分答案相减。
eg.板子数字计数
问题特点:
- 目的是计数
- 限制与数位相关
- 用区间限制
- 上界较大(如 \(10^{18}\))
统计答案可以记搜,也可以用数组递推,从高到低每一位考虑可以填的数字统计答案。
状压
一些状态具有非 \(0\) 即 \(1\) 的状态,在数据规模不大的情况下可以用一个二进制数表示。状压枚举每种状态是 指数级 的复杂度。
状压可以拿来dp,也可以用于更简单地枚举状态。
匈牙利算法
用于解决二分图最大匹配问题。
每次从左部点dfs到右部点:
(1)若右部点没有被匹配,用左部点和它匹配并返回匹配成功。
(2) 若右部点已被匹配,dfs它的匹配点(相当于看能否让它的匹配点换一个右部点匹配),若得到匹配成功的信息,让右部点和当前左部点匹配。
整个过程相当于找一条从左部点以及一条非匹配边开始的增广路。由于非匹配边和匹配边交错出现,每条增广路会让匹配数加一。
bool dfs(int u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(vis[v]) continue;
vis[v]=true;
if(!mat[v]||dfs(mat[v])){
mat[v]=u;
return true;
}
}
return false;
}
网络流(最大流)
一般用 \(Dinic\) 算法,一般也没人专门卡它 除了加强版板子。理论上 \(O(n^2m) \text{(n为点数,m为边数)}\),但实际跑不满,很快。
bool bfs(){
memset(dep,0,sizeof dep);
memcpy(cur,head,sizeof head);
dep[s]=1;
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(!dep[v]&&e[i].f){
dep[v]=dep[u]+1;
if(v==t) return true;
q.push(v);
}
}
}
return dep[t];
}
int dfs(int u,int flow){
if(u==t) return flow;
int used=0;
for(int &i=cur[u];i;i=e[i].nxt){
int v=e[i].v;
if(dep[v]==dep[u]+1&&e[i].f){
int rf=dfs(v,min(flow-used,e[i].f));
used+=rf;
e[i].f-=rf;
e[i^1].f+=rf;
if(used==flow) return used;
}
}
return used;
}
void dinic(){
while(bfs()) ans+=dfs(s,0x7fffffff);
}
\(cur\) 数组是当前弧优化, 必须加上复杂度才正确。
bfs将图分层,保证增广轮数。
其他东西感性理解一下吧

浙公网安备 33010602011771号