洛谷 P1646 [国家集训队]happiness 网络流 最小割 Dinic+当前弧优化
题目链接:
https://www.luogu.com.cn/problem/P1646
参考博客:
https://siyuan.blog.luogu.org/solution-p1646
算法:网络流 最小割 Dinic+当前弧优化
图解:
思路:
1:总量减去最小割即为答案
2:对于每个点 (i,j) ,从 s 连一条容量为选择文科的边,到 t 连一条容量为选择理科的边
3:对于 (i,j) 和 (i+1,j) 两个点的组合情况。假设这两个点同时选文科有 w的喜悦值,我们新建一个节点 x ,从 s 向 x 连一条容量为喜悦值 w的边,再从 x 向 (i,j) 和 (i+1,j) 分别连一条容量为 inf 的边。对于左右前后、文科理科同理
4:每个点自然只能选择一个科目(文科或理科),当某个点选择了文科 s ,那么它向理科 t的边都应该要被断开。考虑哪些边会被断开:首先是它直接连向 t 的边,其次是它和别的点组合连向 t 的边,这样一来,这些边在网络图的割中是有贡献的,意味着这些边的容量在答案中没有贡献
5:即把所有的同学分为两类,要么是选择文科,要么是选择理科,通过割边,将其完全分开
注意:
1:一开始把错误原因归咎于:这个数据范围明明是自己画图,算出来的,但是数据还是开的太小了,真是教训呀
2:真正原因:数据算的没错,是自己手残了,把maxn=4e4+9e3+6e2+2,写成了maxn=1e4+9e3+6e2+2
一:一共20个测试点,9个ac,11个TLE,(45分)用的是自己写的模板Dinic的Dinic函数
#include <bits/stdc++.h>
#define xuhao(i,j) ((i-1)*m+j)
//Dinic+当前弧优化using namespace std;
const int maxn=4e4+9e3+6e2+2,maxm=2e5+7e4+7e3+6e2+1,inf=0x7fffffff;
//一开始把错误原因归咎于:这个数据范围明明是自己画图,算出来的,但是数据还是开的太小了,真是教训呀
//真正原因:数据算的没错,是自己手残了,把maxn=4e4+9e3+6e2+2,写成了maxn=1e4+9e3+6e2+2
//const int maxn=1e5+5,maxm=5e6+5,inf=0x7fffffff;
int n,m,a,s,t,tot=1,head[maxn],dep[maxn],ans,sum,cnt;//用上了分层图,可以用dep判重了
//cur[maxn]
struct edge
{int to,next,w;
}e[maxm];void addedge(int x,int y,int w)
{e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,如果连汇点都搜不到还dfs干什么?
{memset(dep,0,sizeof dep);//一定要初始化
// memcpy(cur,head,sizeof(head));queue<int>q;q.push(s);dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].next){int y=e[i].to,w=e[i].w;if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达{dep[y]=dep[x]+1;q.push(y);}}}return dep[t];//t 的深度不为0,就是搜到了汇点
}int dfs(int x,int flow)
{if(x==t)return flow;int sum=0;for(int i=head[x];i;i=e[i].next){//cur[x]=i;int y=e[i].to,w=e[i].w;if(w&&dep[y]==dep[x]+1)//仅允许流向下一层{int t=dfs(y,min(w,flow));e[i].w-=t;e[i^1].w+=t;flow-=t;sum+=t;}}if(!sum)dep[x]=0;//我与终点(顺着残余网络)不连通的话,那么上一层的点请别给我流量return sum;
}//int dfs(int u,int flow) {
// if(u==t) return flow;
// int ans=0;
// for(int i=head[u];i&&ans<flow;i=e[i].next) {
// // cur[u]=i;
// int v=e[i].to;
// if(e[i].w&&dep[v]==dep[u]+1) {
// int x=dfs(v,min(e[i].w,flow-ans));
// if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;
// }
// }
// if(ans<flow) dep[u]=-1;//说明这个点已经榨干
// return ans;
//}int main()
{ios::sync_with_stdio(0);scanf("%d %d",&n,&m);s=0,t=n*m+2*(n-1)*m+2*n*(m-1)+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(s,xuhao(i,j),a);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(xuhao(i,j),t,a);cnt=n*m;if(n-1!=0){for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i+1,j),inf);}for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i+1,j),cnt,inf);}}if(m-1!=0){for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i,j+1),inf);}for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i,j+1),cnt,inf);}}while(bfs())ans+=dfs(s,inf);printf("%d\n",sum-ans);return 0;
}
二:博客的Dinic函数,也没有+当前弧优化,但是ac啦
#include <bits/stdc++.h>
#define xuhao(i,j) ((i-1)*m+j)
//Dinic+当前弧优化using namespace std;
const int maxn=4e4+9e3+6e2+2,maxm=2e5+7e4+7e3+6e2+1,inf=0x7fffffff;
//一开始把错误原因归咎于:这个数据范围明明是自己画图,算出来的,但是数据还是开的太小了,真是教训呀
//真正原因:数据算的没错,是自己手残了,把maxn=4e4+9e3+6e2+2,写成了maxn=1e4+9e3+6e2+2
//const int maxn=1e5+5,maxm=5e6+5,inf=0x7fffffff;
int n,m,a,s,t,tot=1,head[maxn],dep[maxn],ans,sum,cnt;//用上了分层图,可以用dep判重了
//cur[maxn]
struct edge
{int to,next,w;
}e[maxm];void addedge(int x,int y,int w)
{e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,如果连汇点都搜不到还dfs干什么?
{memset(dep,0,sizeof dep);//一定要初始化
// memcpy(cur,head,sizeof(head));queue<int>q;q.push(s);dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].next){int y=e[i].to,w=e[i].w;if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达{dep[y]=dep[x]+1;q.push(y);}}}return dep[t];//t 的深度不为0,就是搜到了汇点
}//int dfs(int x,int flow)
//{
// if(x==t)return flow;
// int sum=0;
// for(int i=cur[x];i;i=e[i].next)
// {
// cur[x]=i;
// int y=e[i].to,w=e[i].w;
// if(w&&dep[y]==dep[x]+1)//仅允许流向下一层
// {
// int t=dfs(y,min(w,flow));
// e[i].w-=t;e[i^1].w+=t;
// flow-=t;sum+=t;
// }
// }
// if(!sum)dep[x]=0;//我与终点(顺着残余网络)不连通的话,那么上一层的点请别给我流量
// return sum;
//}int dfs(int u,int flow) {if(u==t) return flow;int ans=0;for(int i=head[u];i&&ans<flow;i=e[i].next) {// cur[u]=i;int v=e[i].to;if(e[i].w&&dep[v]==dep[u]+1) {int x=dfs(v,min(e[i].w,flow-ans));if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;}}if(ans<flow) dep[u]=-1;//说明这个点已经榨干return ans;
}int main()
{ios::sync_with_stdio(0);scanf("%d %d",&n,&m);s=0,t=n*m+2*(n-1)*m+2*n*(m-1)+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(s,xuhao(i,j),a);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(xuhao(i,j),t,a);cnt=n*m;if(n-1!=0){for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i+1,j),inf);}for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i+1,j),cnt,inf);}}if(m-1!=0){for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i,j+1),inf);}for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i,j+1),cnt,inf);}}while(bfs())ans+=dfs(s,inf);printf("%d\n",sum-ans);return 0;
}
三:博客的Dinic+当前弧优化 ac
#include <bits/stdc++.h>
#define xuhao(i,j) ((i-1)*m+j)
//Dinic+当前弧优化using namespace std;
const int maxn=4e4+9e3+6e2+2,maxm=2e5+7e4+7e3+6e2+1,inf=0x7fffffff;
//一开始把错误原因归咎于:这个数据范围明明是自己画图,算出来的,但是数据还是开的太小了,真是教训呀
//真正原因:数据算的没错,是自己手残了,把maxn=4e4+9e3+6e2+2,写成了maxn=1e4+9e3+6e2+2
//const int maxn=1e5+5,maxm=5e6+5,inf=0x7fffffff;
int n,m,a,s,t,tot=1,head[maxn],cur[maxn],dep[maxn],ans,sum,cnt;//用上了分层图,可以用dep判重了struct edge
{int to,next,w;
}e[maxm];void addedge(int x,int y,int w)
{e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,如果连汇点都搜不到还dfs干什么?
{memset(dep,0,sizeof dep);//一定要初始化memcpy(cur,head,sizeof(head));queue<int>q;q.push(s);dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].next){int y=e[i].to,w=e[i].w;if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达{dep[y]=dep[x]+1;q.push(y);}}}return dep[t];//t 的深度不为0,就是搜到了汇点
}//int dfs(int x,int flow)
//{
// if(x==t)return flow;
// int sum=0;
// for(int i=cur[x];i;i=e[i].next)
// {
// cur[x]=i;
// int y=e[i].to,w=e[i].w;
// if(w&&dep[y]==dep[x]+1)//仅允许流向下一层
// {
// int t=dfs(y,min(w,flow));
// e[i].w-=t;e[i^1].w+=t;
// flow-=t;sum+=t;
// }
// }
// if(!sum)dep[x]=0;//我与终点(顺着残余网络)不连通的话,那么上一层的点请别给我流量
// return sum;
//}int dfs(int u,int flow) {if(u==t) return flow;int ans=0;for(int i=cur[u];i&&ans<flow;i=e[i].next) {cur[u]=i;int v=e[i].to;if(e[i].w&&dep[v]==dep[u]+1) {int x=dfs(v,min(e[i].w,flow-ans));if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;}}if(ans<flow) dep[u]=-1;//说明这个点已经榨干return ans;
}int main()
{ios::sync_with_stdio(0);scanf("%d %d",&n,&m);s=0,t=n*m+2*(n-1)*m+2*n*(m-1)+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(s,xuhao(i,j),a);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(xuhao(i,j),t,a);cnt=n*m;if(n-1!=0){for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i+1,j),inf);}for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i+1,j),cnt,inf);}}if(m-1!=0){for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i,j+1),inf);}for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i,j+1),cnt,inf);}}while(bfs()){int x;while(x=dfs(s,inf)) ans+=x;}printf("%d\n",sum-ans);return 0;
}
四:博客的Dinic+当前弧优化,与三的区别在于最后如何调用dfs()函数了 ac
#include <bits/stdc++.h>
#define xuhao(i,j) ((i-1)*m+j)
//Dinic+当前弧优化using namespace std;
const int maxn=4e4+9e3+6e2+2,maxm=2e5+7e4+7e3+6e2+1,inf=0x7fffffff;
//一开始把错误原因归咎于:这个数据范围明明是自己画图,算出来的,但是数据还是开的太小了,真是教训呀
//真正原因:数据算的没错,是自己手残了,把maxn=4e4+9e3+6e2+2,写成了maxn=1e4+9e3+6e2+2
//const int maxn=1e5+5,maxm=5e6+5,inf=0x7fffffff;
int n,m,a,s,t,tot=1,head[maxn],cur[maxn],dep[maxn],ans,sum,cnt;//用上了分层图,可以用dep判重了struct edge
{int to,next,w;
}e[maxm];void addedge(int x,int y,int w)
{e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,如果连汇点都搜不到还dfs干什么?
{memset(dep,0,sizeof dep);//一定要初始化memcpy(cur,head,sizeof(head));queue<int>q;q.push(s);dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].next){int y=e[i].to,w=e[i].w;if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达{dep[y]=dep[x]+1;q.push(y);}}}return dep[t];//t 的深度不为0,就是搜到了汇点
}//int dfs(int x,int flow)
//{
// if(x==t)return flow;
// int sum=0;
// for(int i=cur[x];i;i=e[i].next)
// {
// cur[x]=i;
// int y=e[i].to,w=e[i].w;
// if(w&&dep[y]==dep[x]+1)//仅允许流向下一层
// {
// int t=dfs(y,min(w,flow));
// e[i].w-=t;e[i^1].w+=t;
// flow-=t;sum+=t;
// }
// }
// if(!sum)dep[x]=0;//我与终点(顺着残余网络)不连通的话,那么上一层的点请别给我流量
// return sum;
//}int dfs(int u,int flow) {if(u==t) return flow;int ans=0;for(int i=cur[u];i&&ans<flow;i=e[i].next) {cur[u]=i;int v=e[i].to;if(e[i].w&&dep[v]==dep[u]+1) {int x=dfs(v,min(e[i].w,flow-ans));if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;}}if(ans<flow) dep[u]=-1;//说明这个点已经榨干return ans;
}int main()
{ios::sync_with_stdio(0);scanf("%d %d",&n,&m);s=0,t=n*m+2*(n-1)*m+2*n*(m-1)+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(s,xuhao(i,j),a);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a),sum+=a,addedge(xuhao(i,j),t,a);cnt=n*m;if(n-1!=0){for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i+1,j),inf);}for(int i=1;i<=n-1;i++)for(int j=1;j<=m;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i+1,j),cnt,inf);}}if(m-1!=0){for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(s,cnt,a);addedge(cnt,xuhao(i,j),inf);addedge(cnt,xuhao(i,j+1),inf);}for(int i=1;i<=n;i++)for(int j=1;j<=m-1;j++){++cnt;scanf("%d",&a),sum+=a,addedge(cnt,t,a);addedge(xuhao(i,j),cnt,inf);addedge(xuhao(i,j+1),cnt,inf);}}while(bfs())ans+=dfs(s,inf);printf("%d\n",sum-ans);return 0;
}
洛谷 P1646 [国家集训队]happiness 网络流 最小割 Dinic+当前弧优化相关推荐
- 洛谷P2619 [国家集训队]Tree I 题解
洛谷P2619 [国家集训队]Tree I 题解 题目链接:P2619 [国家集训队]Tree I 题意: 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有 need\text{n ...
- 最小割 ---- 集合冲突模型 ----- P1646 [国家集训队]happiness
题面: 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科 ...
- 洛谷P2619 [国家集训队2]Tree I(带权二分,Kruscal,归并排序)
洛谷题目传送门 给一个比较有逼格的名词--WQS二分/带权二分/DP凸优化(当然这题不是DP). 用来解决一种特定类型的问题: 有\(n\)个物品,选择每一个都会有相应的权值,需要求出强制选\(nee ...
- 洛谷P1494 [国家集训队]小Z的袜子
P1494 [国家集训队]小Z的袜子 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- ...
- 洛谷 P4643 [国家集训队]阿狸和桃子的游戏
题目:[国家集训队]阿狸和桃子的游戏 思路: 截个图,这个思路太巨了Orz. 图可以点.
- 洛谷P2839 [国家集训队]middle(主席树)
P2839 [国家集训队]middle 我们可以考虑二分中位数 checkcheckcheck 答案,那么我们对于某个值 midmidmid ,把 [l,r][l,r][l,r] 内的所有小于 mid ...
- 洛谷 - P2774 方格取数问题(最小割-最大流+奇偶拆点)
题目链接:点击查看 题目大意:给出一个n*m的棋盘,每个位置都有一个正整数,现在要从方格中取数,要求任意两个数在方格中的位置不相邻,求取出的数的最大和 题目分析:正难则反,在这个题目中正着建图比较难, ...
- 洛谷 - P1361 - 小M的作物 - 最小割 - 最大权闭合子图
第一次做最小割,不是很理解. https://www.luogu.org/problemnew/show/P1361 要把东西分进两类里,好像可以应用最小割的模板,其中一类A作为源点,另一类B作为汇点 ...
- 洛谷2046 BZOJ2007 NOI2010 海拔 平面图最小割
题目链接 题意: 给你一个网格图,正反边边权不同,从海拔低的地方到海拔高的地方的代价是海拔差乘边权,海拔高到海拔低的地方不需要代价.左上角海拔是0,右下角海拔是1,让你任意安排其他点的海拔,使得每条边 ...
最新文章
- Github标星2.3k+!这个仓库提供了靠谱的入门人工智能的路线及资料!
- 华为三层交换机(5328)DHCP中继应用配置实例
- C#——事件(Event)DEMO[闻鸡起舞]
- python模块下载连接清华镜像的具体步骤_如何下载Pycharm开源版以及设置国内镜像源-百度经验...
- sublime怎么编译php,让sublime编译php、js
- R 读取excel的方法
- vue-cli打包后的思索--代码优化
- Java探索之旅(16)——异常处理
- 年夜饭之 -- 红烧羊肉
- WINDOWS之入侵痕迹清理总结
- android 应用的资源
- 【2030】排队打水问题
- Win11 蓝牙功能消失
- 使用MySQL创建数据库,实现基本SQL语句
- 【Flutter】Dart 数据类型 布尔类型 ( 布尔类型定义 | 逻辑运算 )
- 百度Apollo计划跟踪:感知与预测中神经网络的分析
- 026 Rust死灵书之实现Vec
- 无锡设计培训——室内设计方案图纸包括哪些?
- 如何打造一个高效率的项目团队
- 【饭谈】ChatGpt如果让软件ui都消失的话,那ui自动化测试该何去何从?