题目链接:

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+当前弧优化相关推荐

  1. 洛谷P2619 [国家集训队]Tree I 题解

    洛谷P2619 [国家集训队]Tree I 题解 题目链接:P2619 [国家集训队]Tree I 题意: 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有 need\text{n ...

  2. 最小割 ---- 集合冲突模型 ----- P1646 [国家集训队]happiness

    题面: 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科 ...

  3. 洛谷P2619 [国家集训队2]Tree I(带权二分,Kruscal,归并排序)

    洛谷题目传送门 给一个比较有逼格的名词--WQS二分/带权二分/DP凸优化(当然这题不是DP). 用来解决一种特定类型的问题: 有\(n\)个物品,选择每一个都会有相应的权值,需要求出强制选\(nee ...

  4. 洛谷P1494 [国家集训队]小Z的袜子

    P1494 [国家集训队]小Z的袜子 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- ...

  5. 洛谷 P4643 [国家集训队]阿狸和桃子的游戏

    题目:[国家集训队]阿狸和桃子的游戏 思路: 截个图,这个思路太巨了Orz. 图可以点.

  6. 洛谷P2839 [国家集训队]middle(主席树)

    P2839 [国家集训队]middle 我们可以考虑二分中位数 checkcheckcheck 答案,那么我们对于某个值 midmidmid ,把 [l,r][l,r][l,r] 内的所有小于 mid ...

  7. 洛谷 - P2774 方格取数问题(最小割-最大流+奇偶拆点)

    题目链接:点击查看 题目大意:给出一个n*m的棋盘,每个位置都有一个正整数,现在要从方格中取数,要求任意两个数在方格中的位置不相邻,求取出的数的最大和 题目分析:正难则反,在这个题目中正着建图比较难, ...

  8. 洛谷 - P1361 - 小M的作物 - 最小割 - 最大权闭合子图

    第一次做最小割,不是很理解. https://www.luogu.org/problemnew/show/P1361 要把东西分进两类里,好像可以应用最小割的模板,其中一类A作为源点,另一类B作为汇点 ...

  9. 洛谷2046 BZOJ2007 NOI2010 海拔 平面图最小割

    题目链接 题意: 给你一个网格图,正反边边权不同,从海拔低的地方到海拔高的地方的代价是海拔差乘边权,海拔高到海拔低的地方不需要代价.左上角海拔是0,右下角海拔是1,让你任意安排其他点的海拔,使得每条边 ...

最新文章

  1. Github标星2.3k+!这个仓库提供了靠谱的入门人工智能的路线及资料!
  2. 华为三层交换机(5328)DHCP中继应用配置实例
  3. C#——事件(Event)DEMO[闻鸡起舞]
  4. python模块下载连接清华镜像的具体步骤_如何下载Pycharm开源版以及设置国内镜像源-百度经验...
  5. sublime怎么编译php,让sublime编译php、js
  6. R 读取excel的方法
  7. vue-cli打包后的思索--代码优化
  8. Java探索之旅(16)——异常处理
  9. 年夜饭之 -- 红烧羊肉
  10. WINDOWS之入侵痕迹清理总结
  11. android 应用的资源
  12. 【2030】排队打水问题
  13. Win11 蓝牙功能消失
  14. 使用MySQL创建数据库,实现基本SQL语句
  15. 【Flutter】Dart 数据类型 布尔类型 ( 布尔类型定义 | 逻辑运算 )
  16. 百度Apollo计划跟踪:感知与预测中神经网络的分析
  17. 026 Rust死灵书之实现Vec
  18. 无锡设计培训——室内设计方案图纸包括哪些?
  19. 如何打造一个高效率的项目团队
  20. 【饭谈】ChatGpt如果让软件ui都消失的话,那ui自动化测试该何去何从?

热门文章

  1. 速腾聚创RS-LiDAR激光雷达点云格式转换
  2. 量子计算机九章感想,量子计算机九章这么火,本文让你明白什么是量子
  3. Pytorch中transforms.Compose()的使用
  4. 晶振串联电阻与并联电阻有什么作用?
  5. 港科夜闻|香港科技大学(广州)与中国电信广东公司签署战略合作协议
  6. 数学公式识别神器Mathpix,零错误高效率
  7. 黄金圈理论和知识体系
  8. Oracle等数据库报盘步骤(使用脚本批量压缩)
  9. php 新手二维码生成
  10. RTX2070s和RTX2060s显卡差距大吗?