BZOJ5120 [2017国家集训队测试]无限之环 费用流
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ5120
题意概括
原题挺简略的。
题解
本题好难。
听了任轩笛大佬<国家队神犇>的讲课才略会。
然而费用流我也是第一次写。而且这题的费用流是特殊的(简化的)。
于是我抄了任爷的代码。
然而,我因为常量写错,找了一个小时……
这里的work和add我都是直接抄的……懒得打,打完还不一定找得出。反正做法是懂了。
本题很坑。
对于40分,还是比较好拿的,插头dp+滚动(然而我忘记开滚动炸了内存……)就可以了。(代码在最后)
据说插头dp+map可以卡到100?
这里讲标算做法。
我们考虑网络流。
对于两个管口相接,我们只需要建一条流量为1的边。
但是每一个格子的管子都会转。(除了直线)
然而旋转需要费用。
所以我们考虑在自身旋转后,建边要加上费用。
我们设一个格子的4个接口分别为a,b,c,d,如下图:
如果分情况讨论,那么实在麻烦。实际上我们可以通过自身连边来完成旋转。
首先我们考虑管子的本质情况:有7种。那么我们可以有相印的连边方案。(二元组中左边为容量,右边为费用)
这个……自身建图是不用的。
这个,以及下面的那个建不出图,所以题目说了不能转;哈哈哈哈哈哈哈
还有一种情况是空的,那么也是不需要自身转移的。
通过自身转移的建图,我们就可以通过费用来完成旋转。
然后就是源点的建边,汇点的建边。
当然,别忘记,对于每一个格子。
然而,我们要记得连上连接接口的边。
但是我们发现,这个不仔细考虑会错掉。
我们把格子按照(行号+列号)的奇偶性黑白染色。
我们发现黑格子的管子一定是接上白格子的。
于是我们人为规定,让流从黑格子流向白格子。
这样就能确定某一个节点(其实就是方格边上的4点)是从源点流来还是流向汇点了。
最后感叹一句:这样做真妙啊!
代码
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
typedef long long LL;
const int NM=2005,N=8205,M=N*20,Inf=2333333;
int dx[4]={-1, 0, 1, 0};
int dy[4]={ 0, 1, 0,-1};
struct Edge{
int x,y,z,flow,nxt;
Edge (){}
Edge (int a,int b,int c,int d,int e){
x=a,y=b,z=c,flow=d,nxt=e;
}
}E[M];
int n,m,S,T,cnt,tot,res,ans,sum;
int id[NM][NM][4],dis[N],vis[N],fst[N],Q[N],pre[N];
int cnt_bit_1(int x){
int ans=0;
while (x)
ans+=x&1,x>>=1;
return ans;
}
bool check_block(int x,int y){
return 1<=x&&x<=n&&1<=y&&y<=m;
}
void Add_Edge(int x,int y,int flow,int z){
E[++cnt]=Edge(x,y,z,flow,fst[x]),fst[x]=cnt;
E[++cnt]=Edge(y,x,-z,0,fst[y]),fst[y]=cnt;
}
void Add(int x,int y,int flow,int z,int color){
if (color==1)
Add_Edge(x,y,flow,z);
else if (x==S)
Add_Edge(y,T,flow,z);
else
Add_Edge(y,x,flow,z);
}
void work(int i,int j,int type,int dir,int color){
int *A=id[i][j];
if (type==1){
Add(S,A[dir],1,0,color);
Add(A[dir],A[(dir+1)%4],1,1,color);
Add(A[dir],A[(dir+2)%4],1,2,color);
Add(A[dir],A[(dir+3)%4],1,1,color);
}
if (type==2){
Add(S,A[dir],1,0,color);
Add(S,A[(dir+1)%4],1,0,color);
Add(A[dir],A[(dir+2)%4],1,1,color);
Add(A[(dir+1)%4],A[(dir+3)%4],1,1,color);
}
if (type==3){
Add(S,A[dir],1,0,color);
Add(S,A[(dir+2)%4],1,0,color);
}
if (type==4){
Add(S,A[dir],1,0,color);
Add(S,A[(dir+1)&3],1,0,color);
Add(S,A[(dir+2)&3],1,0,color);
Add(A[dir],A[(dir+3)&3],1,1,color);
Add(A[(dir+1)&3],A[(dir+3)&3],1,2,color);
Add(A[(dir+2)&3],A[(dir+3)&3],1,1,color);
}
if (type==5){
Add(S,A[dir],1,0,color);
Add(S,A[(dir+1)%4],1,0,color);
Add(S,A[(dir+2)%4],1,0,color);
Add(S,A[(dir+3)%4],1,0,color);
}
}
bool SPFA(){
int head=0,tail=0,qmod=8191;
int x,y;
memset(vis,0,sizeof vis);
memset(dis,63,sizeof dis);
memset(pre,0,sizeof pre);
Q[tail=(tail+1)&qmod]=S;
dis[S]=0,vis[S]=1,pre[S]=0;
while (head!=tail){
vis[x=Q[head=(head+1)&qmod]]=0;
for (register int i=fst[x];i;i=E[i].nxt)
if (E[i].flow&&dis[x]+E[i].z<dis[y=E[i].y]){
dis[y]=dis[x]+E[i].z;
pre[y]=i;
if (!vis[y]){
vis[y]=1;
Q[tail=(tail+1)&qmod]=y;
}
}
}
return dis[T]<Inf;
}
void Flowing(){
for (register int i=pre[T];i;i=pre[E[i].x])
E[i].flow--,E[i^1].flow++;
ans++,res+=dis[T];
}
int main(){
scanf("%d%d",&n,&m);
memset(fst,0,sizeof fst);
tot=sum=0,cnt=1;
S=++tot,T=++tot;
for (int i=1;i<=n;i++)
for (int j=1,x;j<=m;j++){
scanf("%d",&x);
for (int k=0;k<4;k++)
id[i][j][k]=++tot;
if (x==1) work(i,j,1,0,(i+j)&1);
if (x==2) work(i,j,1,1,(i+j)&1);
if (x==3) work(i,j,2,0,(i+j)&1);
if (x==4) work(i,j,1,2,(i+j)&1);
if (x==5) work(i,j,3,0,(i+j)&1);
if (x==6) work(i,j,2,1,(i+j)&1);
if (x==7) work(i,j,4,0,(i+j)&1);
if (x==8) work(i,j,1,3,(i+j)&1);
if (x==9) work(i,j,2,3,(i+j)&1);
if (x==10) work(i,j,3,1,(i+j)&1);
if (x==11) work(i,j,4,3,(i+j)&1);
if (x==12) work(i,j,2,2,(i+j)&1);
if (x==13) work(i,j,4,2,(i+j)&1);
if (x==14) work(i,j,4,1,(i+j)&1);
if (x==15) work(i,j,5,0,(i+j)&1);
sum+=cnt_bit_1(x);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1)
for (int k=0;k<4;k++){
int x=i,y=j;
int x_=i+dx[k],y_=j+dy[k];
if (!check_block(x_,y_))
continue;
Add_Edge(id[x][y][k],id[x_][y_][(k+2)%4],1,0);
}
ans=res=0;
while (SPFA())
Flowing();
printf("%d",(ans*2==sum)?res:-1);
return 0;
}
40分的插头dp
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=2005,M=20,S=1<<17,Inf=100000;
int n,m,b[N],a[N],dp[2][S];
int Ha(int a,int b){
//1<=a<=n,1<=b<=m
return (a-1)*m+b-1 + 1;
}
void Hab(int v,int &a,int &b){
v--;
a=v/m+1;
b=v%m+1;
}
int rotate(int v,int k){
return ((v>>k)|(v<<(4-k)))&15;
}
int main(){
bool tag=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&b[Ha(i,j)]);
if (n<m)
tag=1;
if (tag==1){
swap(n,m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[Ha(i,j)]=rotate(b[(m-j)*n+i],3);/*b[(m-j)*n+i]*/
// (m-j+1,i)
}
else
for (int i=1;i<=n*m;i++)
a[i]=b[i];
/* for (int i=1;i<=n;i++,puts(""))
for (int j=1;j<=m;j++)
printf("%d ",a[Ha(i,j)]);*/
memset(dp,63,sizeof dp);
dp[0][0]=0;
int s=1<<(m+1),x1=0,x0=1;
for (int i=1;i<=n*m;i++){
x1^=1,x0^=1;
memset(dp[x1],63,sizeof dp[x1]);
int x,y,cost;
Hab(i,x,y);
for (int j=0;j<s;j++){
if (dp[x0][j]>Inf)
continue;
if (a[i]==5){
if ((j&1)||!((j>>y)&1))
continue;
dp[x1][j]=min(dp[x1][j],dp[x0][j]);
continue;
}
if (a[i]==10){
if (y==m)
continue;
if (!(j&1)||((j>>y)&1))
continue;
dp[x1][j]=min(dp[x1][j],dp[x0][j]);
continue;
}
for (int k=0;k<4;k++){
int dir=rotate(a[i],k);
int a1=dir&1,a2=(dir>>1)&1,a3=(dir>>2)&1,a4=(dir>>3)&1;
if ((a1^((j>>y)&1))||(a4^(j&1))||(y==m&&a2))
continue;
int j_=((j&(s-2))|a2)&(s-1-(1<<y))|(a3<<y);
dp[x1][j_]=min(dp[x1][j_],dp[x0][j]+(k==3?1:k));
}
}
}
/* for (int i=0;i<=n*m;i++,puts(""))
for (int j=0;j<s;j++)
printf("%10d ",dp[i][j]);*/
int ans=dp[x1][0];
if (ans>Inf)
ans=-1;
printf("%d",ans);
return 0;
}
BZOJ5120 [2017国家集训队测试]无限之环 费用流的更多相关文章
- BZOJ 5120: [2017国家集训队测试]无限之环(费用流)
传送门 解题思路 神仙题.调了一个晚上+半个上午..这道咋看咋都不像图论的题竟然用费用流做,将行+列为奇数的点和偶数的点分开,也就是匹配问题,然后把一个点复制四份,分别代表这个点的上下左右接头,如果有 ...
- [BZOJ5120] [2017国家集训队测试]无限之环
Description 曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏: 游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中 ...
- bzoj 5120: [2017国家集训队测试]无限之环【最小费用最大流】
玄妙的建图-- 这种平衡度数的题按套路是先黑白染色然后分别连ST点,相邻格子连黑向白连费用1流量0的边,然后考虑费用怎么表示 把一个点拆成五个,上下左右中,中间点黑白染色连ST, 对于连S的点,中点连 ...
- bzoj 5120 [2017国家集训队测试]无限之环——网络流
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120 旋转的话相当于去掉一个插头.新增一个插头,所以在这两个插头之间连边并带上费用即可. 网 ...
- BZOJ.5120.[清华集训2017]无限之环(费用流zkw 黑白染色)
题目链接 LOJ 洛谷 容易想到最小费用最大流分配度数. 因为水管形态固定,每个点还是要拆成4个点,分别当前格子表示向上右下左方向. 然后能比较容易地得到每种状态向其它状态转移的费用(比如原向上的可以 ...
- BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路
BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路 Description 虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物 ...
- 【BZOJ2622】[2012国家集训队测试]深入虎穴 次短路
[BZOJ2622][2012国家集训队测试]深入虎穴 Description 虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物,例如“ ...
- 2017国家集训队作业Atcoder题目试做
2017国家集训队作业Atcoder题目试做 虽然远没有达到这个水平,但是据说Atcoder思维难度大,代码难度小,适合我这种不会打字的选手,所以试着做一做 不知道能做几题啊 在完全自己做出来的题前面 ...
- 2017国家集训队作业[agc016b]Color Hats
2017国家集训队作业[agc016b]Color Hats 题意: 有\(N\)个人,每个人有一顶帽子.帽子有不同的颜色.现在,每个人都告诉你,他看到的所有其它人的帽子共有多少种颜色,问有没有符合所 ...
随机推荐
- 【原创】angularjs1.3.0源码解析之执行流程
Angular执行流程 前言 发现最近angularjs在我厂的应用变得很广泛,下周刚好也有个angular项目要着手开始做,所以先做了下功课,从源代码开始入手会更深刻点,可能讲的没那么细,侧重点在于 ...
- 如何学好web安全
web服务组件: 横向就是如图所示,纵向就是数据流:数据流说白了就是http协议. 举例: 1.如果在操作系统没有处理好,就产生了OS命令执行的安全问题: 2.如果在存储层的数据库中没有处理好,数据库 ...
- BZOJ3527 [Zjoi2014]力 【fft】
题目 给出n个数qi,给出Fj的定义如下: 令Ei=Fi/qi,求Ei. 输入格式 第一行一个整数n. 接下来n行每行输入一个数,第i行表示qi. 输出格式 n行,第i行输出Ei.与标准答案误差不超过 ...
- [C++]数组与指针[二维数组与指针]
- tidb 架构 ~Tidb学习系列(1)
一 简介:今天来研究Tidb 二 安装测试: 0 下载Tidb wget http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz 按如 ...
- Django学习手册 - 正则URL路由配置/路由分发
############################################### 总结: 一.url路由配置: 方式一:(通过url链接get获取) 方式二:(url路由匹配方式获取-拓 ...
- atof()函数 atol()
atof()函数 atof():double atof(const char *str ); 功 能: 把字符串转换成浮点数 str:要转换的字符串. 返回值:每个函数返回 double 值,此值由将 ...
- Kaggle Titanic补充篇
1.关于年龄Age 除了利用平均数来填充,还可以利用正态分布得到一些随机数来填充,首先得到已知年龄的平均数mean和方差std,然后生成[ mean-std, mean+std ]之间的随机数,然后 ...
- vim 超强发行版
推荐第一个: https://github.com/spf13/spf13-vim https://github.com/Spacevim/Spacevim https://github.com/JB ...
- php数据库的增删改查
1.查询: 数据的显示,这里就可以嵌入php来进行数据的输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ...