[2018HN省队集训D5T1] 沼泽地marshland
[2018HN省队集训D5T1] 沼泽地marshland
题意
给定一张 \(n\times n\) 的棋盘, 对于位置 \((x,y)\), 若 \(x+y\) 为奇数则可能有一个正权值. 你可以在棋盘上互不重叠地任意放置最多 \(m\) 个L形三骨牌, 放置后骨牌拐角处的格子权值清零. 其中 \(k\) 个格子是障碍且障碍处权值必定为 \(0\). 最小化权值总和.
\(n\le 50\).
题解
这种乍一看像插头DP但是又让你求最优解而不是计数的棋盘题多半就是网络流了.
首先拐角的地方如果不是带权点的话这张骨牌没卵用, 所以我们只算拐角是带权点的骨牌. 容易发现覆盖一个带权点时必定会覆盖两个共顶点的无权点, 不难想到在无权点之间连权值为对应带权点的边然后跑最大权匹配.
无权点其实也组成了一个网格图(转 \(45^\circ\) 就看出来了), 所以可以黑白染色跑费用流. (实际上就是按奇偶行分类)
但是这样可能会出现重复计算某个带权点的贡献的情况. 所以我们必须要让包含同一个带权点的情况互斥. 只要加一对点在中间连一条容量为 \(1\) 权值为带权点的值的边, 然后再把它周围的点都用这对点收束到一起就好了.
或者说这是个三分图匹配?
建完图限制一下流量不超过 \(m\) 跑最小(大)费用流就好了. 用总和减去覆盖掉的权值.
参考代码
#include <bits/stdc++.h>
const int MAXN=110;
const int MAXV=1e4+10;
const int MAXE=3e5+10;
const int INFI=0x7F7F7F7F;
const int d[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
struct Edge{
int from;
int to;
int dis;
int flow;
Edge* rev;
Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* top=E;
int n;
int m;
int k;
int dis[MAXV];
bool vis[MAXV];
bool inq[MAXV];
int a[MAXN][MAXN];
int id[MAXN][MAXN];
int idx[MAXN][MAXN];
bool blk[MAXN][MAXN];
int Dinic(int,int);
bool SPFA(int,int);
int DFS(int,int,int);
void Insert(int,int,int,int);
int main(){
scanf("%d%d%d",&n,&m,&k);
int sum=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",a[i]+j);
sum+=a[i][j];
}
}
for(int i=0;i<k;i++){
int x,y;
scanf("%d%d",&x,&y);
blk[x][y]=true;
}
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
id[i][j]=++cnt;
if((i^j)&1)
idx[i][j]=++cnt;
}
}
int s=0,t=cnt+1,ss=cnt+2;
Insert(ss,s,0,m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!blk[i][j]){
if((i^j)&1){
Insert(id[i][j],idx[i][j],-a[i][j],1);
for(int k=0;k<4;k++){
std::pair<int,int> a(i+d[k][0],j+d[k][1]),b(i+d[(k+1)%4][0],j+d[(k+1)%4][1]);
if(id[a.first][a.second]&&id[b.first][b.second]){
if(a.first&1){
Insert(id[a.first][a.second],id[i][j],0,1);
Insert(idx[i][j],id[b.first][b.second],0,1);
}
else{
Insert(id[b.first][b.second],id[i][j],0,1);
Insert(idx[i][j],id[a.first][a.second],0,1);
}
}
}
}
else{
if(i&1)
Insert(s,id[i][j],0,1);
else
Insert(id[i][j],t,0,1);
}
}
}
}
printf("%d\n",sum+Dinic(ss,t));
return 0;
}
int Dinic(int s,int t){
int ans=0;
while(SPFA(s,t))
ans+=dis[t]*DFS(s,INFI,t);
return ans;
}
int DFS(int s,int flow,int t){
if(s==t||flow<=0)
return flow;
int rest=flow;
vis[s]=true;
for(Edge* i=head[s];i!=NULL;i=i->next){
if(i->flow>0&&dis[i->to]==dis[s]+i->dis&&!vis[i->to]){
int tmp=DFS(i->to,std::min(rest,i->flow),t);
rest-=tmp;
i->flow-=tmp;
i->rev->flow+=tmp;
}
}
return flow-rest;
}
bool SPFA(int s,int t){
memset(dis,0x7F,sizeof(dis));
memset(vis,0,sizeof(vis));
std::queue<int> q;
q.push(s);
dis[s]=0;
vis[s]=true;
while(!q.empty()){
s=q.front();
q.pop();
vis[s]=false;
for(Edge* i=head[s];i!=NULL;i=i->next){
if(i->flow>0&&dis[i->to]>dis[s]+i->dis){
dis[i->to]=dis[s]+i->dis;
if(!vis[i->to]){
q.push(i->to);
vis[i->to]=true;
}
}
}
}
return dis[t]<0;
}
inline void Insert(int from,int to,int dis,int flow){
top->from=from;
top->to=to;
top->dis=dis;
top->flow=flow;
top->rev=top+1;
top->next=head[from];
head[from]=top++;
top->from=to;
top->to=from;
top->dis=-dis;
top->flow=0;
top->rev=top-1;
top->next=head[to];
head[to]=top++;
}
[2018HN省队集训D5T1] 沼泽地marshland的更多相关文章
- [2018HN省队集训D9T1] circle
[2018HN省队集训D9T1] circle 题意 给定一个 \(n\) 个点的竞赛图并在其中钦定了 \(k\) 个点, 数据保证删去钦定的 \(k\) 个点后这个图没有环. 问在不删去钦定的这 \ ...
- [2018HN省队集训D8T1] 杀毒软件
[2018HN省队集训D8T1] 杀毒软件 题意 给定一个 \(m\) 个01串的字典以及一个长度为 \(n\) 的 01? 序列. 对这个序列进行 \(q\) 次操作, 修改某个位置的字符情况以及查 ...
- [2018HN省队集训D8T3] 水果拼盘
[2018HN省队集训D8T3] 水果拼盘 题意 给定 \(n\) 个集合, 每个集合包含 \([1,m]\) 中的一些整数, 在这些集合中随机选取 \(k\) 个集合, 求这 \(k\) 个集合的并 ...
- [2018HN省队集训D6T2] girls
[2018HN省队集训D6T2] girls 题意 给定一张 \(n\) 个点 \(m\) 条边的无向图, 求选三个不同结点并使它们两两不邻接的所有方案的权值和 \(\bmod 2^{64}\) 的值 ...
- [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...
- [2018HN省队集训D5T2] party
[2018HN省队集训D5T2] party 题意 给定一棵 \(n\) 个点以 \(1\) 为根的有根树, 每个点有一个 \([1,m]\) 的权值. 有 \(q\) 个查询, 每次给定一个大小为 ...
- [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard
[Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard 题意 给定一个 \(n\times n\) 的矩阵 \(A\), (\(n\) 为奇数) , ...
- [2018HN省队集训D1T3] Or
[2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...
- [2018HN省队集训D1T1] Tree
[2018HN省队集训D1T1] Tree 题意 给定一棵带点权树, 要求支持下面三种操作: 1 root 将 root 设为根. 2 u v d 将以 \(\operatorname{LCA} (u ...
随机推荐
- DES对 json 、http参数加密解密算法
网上众多大神们的众多方式实现加解密操作及保障数据安全性.今天无意中发现一篇以 DES加密解密算法.摘抄如下 工具类: import java.security.SecureRandom; import ...
- 功能------常用快捷键(在win10下)
功能------win10 常用快捷键 在进行学习,记录,编写代码时,需要用到一些功能,用鼠标浪费时间,可以使用快捷键来快速的处理.方便操作. 以下内容分为两类.快捷键以及触控板类(不能享用鼠标) 快 ...
- Dubbo2.7源码分析-如何发布服务
Dubbo的服务发布逻辑是比较复杂的,我还是以Dubbo自带的示例讲解,这样更方便和容易理解. Provider配置如下: <?xml version="1.0" encod ...
- 什么是汉明窗?加Hanmming窗的作用?
什么是汉明窗?加Hanmming窗的作用? 1.什么是汉明窗? 答:我是做语音识别的,我就从语音的角度跟你说一下吧. 语音信号一般在10ms到30ms之间,我们可以把它看成是平稳的.为了处理语音信号, ...
- vue权限路由实现方式总结二
之前已经写过一篇关于vue权限路由实现方式总结的文章,经过一段时间的踩坑和总结,下面说说目前我认为比较"完美"的一种方案:菜单与路由完全由后端提供. 菜单与路由完全由后端返回 这种 ...
- Java中泛型通配符的一点概念
以List<T>为例,通常如果我们想在List中存储某种类型的数据,我们会用下面的语法进行声明和使用: List<String> allMsg = new ArrayList& ...
- MySQL5:触发器
什么是触发器 MySQL的触发器(trigger)和存储过程一样,都是嵌入到MySQL中的 一段程序.触发器是由事件来触发某个操作,这些事件包括INSERT.UPDATE和DELETE语句.如果定义了 ...
- 《码出高效 Java开发手册》第三章 代码风格
第3章 代码风格 3.1 命名 符合语言特性 体现代码元素特征: Abstract xxx. Basexxxx.xxException.xxxTest等; 包名统一使用小写, 完整单词+点分隔符; 枚 ...
- item2 快捷键
快捷键 ⌘ + Click:可以打开文件,文件夹和链接 ⌘ + n:新建窗口 ⌘ + t:新建标签页 ⌘ + w:关闭当前页 ⌘ + 数字 & ⌘ + 方向键:切换标签页 ⌥⌘ + 数字:切换 ...
- JAVA虚拟机的生命周期
一个运行时的Java虚拟机实例的天职是:负责运行一个java程序.当启动一个Java程序时,一个虚拟机实例也就诞生了.当该程序关闭退出,这个虚拟机实例也就随之消亡.如果同一台计算机上同时运行三个Jav ...