luogu 4142
2024-10-21 10:25:11
费用流好题
本题的建图很有意思
正常我们看到棋盘问题应该先对整个棋盘黑白染色构成一个二分图,然后再考虑建图的问题
但是本题题目中已经明确区分了不同的斜线,问题在于怎么保证一个"L"形
因此我们进一步分析:显然柱子应该放在有代价的位置,我们应该由这样的位置向上下左右连边,保证有两个就行
但是这样建图是有问题的,因为首先,难以保证选了两个位置,其次,上下/左右这种选两个的方式也是不合法的!
因此我们考虑进一步拆解这个图:我们把这个图分成三部分(请自动忽略坏点),一部分是产生代价的地方,一部分是行列号均为偶数的部分,一部分是行列号均为奇数的部分
然后我们这样建图:源点->二级源点->行列号均为偶数的部分->产生代价的部分->行列号均为奇数的部分->汇点
其中二级源点是为了限制流量不得大于$m$的
这样连边之后,就很好的保证了必须选两个且排除了选上下两个/左右两个的情况
中间的点需要拆点,拆出来两个点流量为1,费用给出
注意跑的是最大费用流,跑出来以后还要用总费用减去这个最大费用,同时我们并不需要让流量最大,因此如果费用为负直接跳出
贴代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int inf=0x3f3f3f3f;
struct Edge
{
int nxt;
int to;
int val;
int pri;
}edge[5000005];
int lim[10005];
int to[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
bool vis[55][55];
bool used[10005];
int dis[10005];
int w[55][55];
int val[10005];
int head[10005];
int pre[10005],f[10005];
int tot=0,cnt=1;
int n,m,k;
int ans=0;
int st,ed,eed;
int idx(int x,int y)
{
return (x-1)*n+y;
}
int ide(int x)
{
return x&1?x+1:x-1;
}
bool check(int x,int y)
{
return x>0&&x<=n&&y>0&&y<=n&&!vis[x][y];
}
void add(int l,int r,int z,int v)
{
edge[cnt].nxt=head[l];
edge[cnt].to=r;
edge[cnt].val=z;
edge[cnt].pri=v;
head[l]=cnt++;
}
void dadd(int l,int r,int z,int v)
{
add(l,r,z,v),add(r,l,0,-v);
}
bool spfa()
{
memset(dis,-0x3f,sizeof(dis));
memset(pre,-1,sizeof(pre));
memset(f,0,sizeof(f));
dis[tot+1]=0;
used[tot+1]=1;
lim[tot+1]=inf;
queue <int> M;
M.push(tot+1);
while(!M.empty())
{
int u=M.front();
M.pop();
for(int i=head[u];i;i=edge[i].nxt)
{
int to=edge[i].to;
if(edge[i].val&&dis[to]<dis[u]+edge[i].pri)
{
dis[to]=dis[u]+edge[i].pri;
lim[to]=min(lim[u],edge[i].val);
f[to]=u,pre[to]=i;
if(!used[to])M.push(to),used[to]=1;
}
}
used[u]=0;
}
return dis[tot+3]>0&&pre[tot+3]!=-1;
}
int EK()
{
int maxf=0,maxv=0;
while(spfa())
{
maxf+=lim[tot+3],maxv+=lim[tot+3]*dis[tot+3];
int temp=tot+3;
while(temp!=tot+1)
{
edge[pre[temp]].val-=lim[tot+3];
edge[ide(pre[temp])].val+=lim[tot+3];
temp=f[temp];
}
}
return maxv;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
tot=n*n*2;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&w[i][j]),ans+=w[i][j];
dadd(tot+1,tot+2,m,0);
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
vis[x][y]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(vis[i][j])continue;
dadd((idx(i,j)<<1)-1,idx(i,j)<<1,1,w[i][j]);
if((i&1)&&(j&1))dadd(idx(i,j)<<1,tot+3,1,0);
else if(!(i&1)&&!(j&1))
{
dadd(tot+2,(idx(i,j)<<1)-1,1,0);
for(int t=0;t<4;t++)
{
int toi=i+to[t][0],toj=j+to[t][1];
if(!check(toi,toj))continue;
dadd(idx(i,j)<<1,(idx(toi,toj)<<1)-1,1,0);
}
}else
{
for(int t=0;t<4;t++)
{
int toi=i+to[t][0],toj=j+to[t][1];
if(!check(toi,toj)||!(toi&1)||!(toj&1))continue;
dadd(idx(i,j)<<1,(idx(toi,toj)<<1)-1,1,0);
}
}
}
}
printf("%d\n",ans-EK());
return 0;
}
最新文章
- 空的安卓工程添加activity
- Java Web开发之Servlet获取ckeditor内容
- OpenCV图像轮廓检测
- CentOS 7 网络配置方法
- Rhel6-tomcat+nginx+memcached配置文档
- Android handler Thread 修改UI Demo
- LR 取到怎么样才能得到参数列表中的每一个值
- mobx react
- POJ 2151 Check the difficulty of problems (动态规划-可能DP)
- ILMerge 简单使用
- linux下php开发环境搭建(nginx+php+mysql)
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例
- ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十六节--SignalR与ABP框架Abp.Web.SignalR及扩展
- Linux学习之CentOS(十三)-----磁盘管理之 磁盘与目录的容量(转) df 与du 命令
- 第三十七节、人脸检测MTCNN和人脸识别Facenet(附源码)
- JS中lambda表达式的优缺点和使用场景(转)
- WEB前端问题——img标签的onclick事件无法响应问题【转载】
- Xcode 去掉控制台无用打印信息
- 虚函数指针sizeof不为sizeof(void*)
- 解决ThinkPHP的Create方法失效而没有提示错误信息的问题
热门文章
- shell_Day06
- 你的ASP.NET Pages项目编译时为何总是很慢慢慢~?
- js var
- 日常开发记录-this.$message,this.$prompt,交换弹窗确定和取消按钮的位置,确定在左,取消在右
- @Component类相互引用的加载顺序
- git 代码提交到github 回滚到上一版本
- https原理(四)双向实践(java客户端+tcp代理)
- 国际版Office365客户端配置
- Ubuntu下CodeBlocks控制台程序中文显示乱码解决问题
- postman或浏览器可以访问,java不能访问的post请求,连接超时