AtCoder AGC031E Snuke the Phantom Thief (费用流)

2024-02-15 15:32

本文主要是介绍AtCoder AGC031E Snuke the Phantom Thief (费用流),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接

https://atcoder.jp/contests/agc031/tasks/agc031_e

题解

做法一(我的做法)

这是我yy出来的一个上下界费用流做法,自己没找到什么反例,能过。(一开始一直WA以为做法假了结果发现写错了一个sb地方摔)如果有什么问题敬请指出,谢谢。
考虑一维怎么做,首先可以枚举一共选多少个,那么对每个位置的限制就相当于“前\(i\)个里选的个数在\([L_i,R_i]\)之间”。
我们可以给每个位置建一个点,源点往\(1\)号位置的点连\(([0,+\infty],0)\), \(i\)\((i+1)\)\(([L_i,R_i],0)\), \(i\)往汇点连\(([0,1],w_i)\), 跑最大费用最大流即可。
然后拓展到二维:我们给每一行每一列分别建一个点,记作\(R_i,C_j\). 对每个物品\(u\), 从\(R_{x_u}\)\(C_{y_u}\)\(([0,1],w_u)\). 设限制形如“从第\(i\)行到最后一行选的个数在\([LX_i,RX_i]\)之间”、“前\(j\)列选的个数在\([LY_j,RY_j]\)之间”,则从\(R_{i-1}\)\(R_i\)\(([LX_i,RX_i],0)\) (特别地,\(i=0\)时起点为源点),从\(C_j\)\(C_{j+1}\)\(([LY_j,RY_j],0)\) (特别地,\(j=MX\)时终点为汇点,\(MX\)为最大坐标),跑最大费用流就可以了。
这里上下界网络流因为最大流已知,我采用的是把每条边的下界费用设为无穷大的写法,需要使用__int128. 有其他的写法但是博主太菜了不会……
时间复杂度\(O(n\cdot MFMC(2n,5n))\). (一共要建\(3n\)条边,其中至多\(2n\)条带上下界)

做法二(官方题解)

在路上了。

代码

做法一

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define pii pair<int,int>
#define riterator reverse_iterator
using namespace std;inline int read()
{int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f;
}namespace NetFlow
{const int N = 206;const int M = 284;#define lllong __int128const lllong INF = 2e17;const lllong INF2 = 1e22;struct AEdge{int u,v,wl,wr; lllong c;} ae[M+3];struct Edge{int u,v,nxt,w; lllong c;} e[(M<<2)+3];int fe[N+3];lllong dis[N+3];int que[N+5];bool inq[N+3];int lst[N+3];int n,m,en,s,t; lllong mf,mc;void init(){memset(ae,0,sizeof(ae)); memset(e,0,sizeof(e)); memset(fe,0,sizeof(fe)); memset(dis,0,sizeof(dis)); memset(que,0,sizeof(que)); memset(inq,0,sizeof(inq)); memset(lst,0,sizeof(lst));n = m = s = t = 0; mf = mc = (lllong)0;}void addedge0(int u,int v,int w,lllong c){en++; e[en].u = u,e[en].v = v,e[en].w = w,e[en].c = c;e[en].nxt = fe[u]; fe[u] = en;en++; e[en].u = v,e[en].v = u,e[en].w = 0,e[en].c = -c;e[en].nxt = fe[v]; fe[v] = en;}bool spfa(){for(int i=1; i<=n; i++) dis[i] = -INF2;int hd = 1,tl = 2; que[1] = s; dis[1] = 0;while(hd!=tl){int u = que[hd]; hd++; if(hd>n+1) hd-=n+1;for(int i=fe[u]; i; i=e[i].nxt){int v = e[i].v;if(e[i].w>0&&dis[e[i].v]<dis[u]+e[i].c){dis[e[i].v] = dis[u]+e[i].c; lst[e[i].v] = i;if(!inq[e[i].v]){inq[e[i].v] = true;que[tl] = e[i].v; tl++; if(tl>n+1) tl-=n+1;}}}inq[u] = false;}return dis[t]!=-INF2;}void calcflow(){int flow = 1e5;for(int u=t; u!=s; u=e[lst[u]].u){flow = min(flow,e[lst[u]].w);}for(int u=t; u!=s; u=e[lst[u]].u){e[lst[u]].w -= flow; e[lst[u]^1].w += flow;}mf += flow; mc += dis[t]*(lllong)flow;}void mfmc(){mf = mc = 0; while(spfa()) {calcflow();}}void addedge(int u,int v,int wl,int wr,llong c){m++; ae[m].u = u,ae[m].v = v,ae[m].wl = wl,ae[m].wr = wr,ae[m].c = c;}llong flow(int _n,int _s,int _t,int _mf){n = _n,s = _s,t = _t; en = 1; lllong ret = 0ll;for(int i=1; i<=m; i++){if(ae[i].wl) {addedge0(ae[i].u,ae[i].v,ae[i].wl,INF); ret += (ae[i].c-INF)*(lllong)ae[i].wl;}addedge0(ae[i].u,ae[i].v,ae[i].wr-ae[i].wl,ae[i].c);}mfmc();if(mf!=_mf||mc+ret<(lllong)0) {return -1ll;}return mc+ret;}
}const int N = 100;
struct Element
{int x,y; llong w;
} a[N+3];
struct Condition
{int typ,x,y;
} b[N*4+3];
int alx[N+3],aly[N+3],arx[N+3],ary[N+3];
int n,m,mx; llong ans;int main()
{scanf("%d",&n);for(int i=1; i<=n; i++){scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].w); mx = max(mx,max(a[i].x,a[i].y));}scanf("%d",&m);for(int i=1; i<=m; i++){char str[5]; scanf("%s%d%d",str,&b[i].x,&b[i].y); mx = max(mx,b[i].x);b[i].typ = (str[0]=='L'?0:(str[0]=='R'?1:(str[0]=='D'?2:3)));}mx++;for(int k=1; k<=n; k++){for(int i=0; i<=mx; i++) alx[i] = 0,arx[i] = k,aly[i] = 0,ary[i] = k;for(int i=1; i<=m; i++){if(b[i].typ==0){alx[b[i].x+1] = max(alx[b[i].x+1],k-b[i].y);}else if(b[i].typ==1){arx[b[i].x] = min(arx[b[i].x],b[i].y);}else if(b[i].typ==2){ary[b[i].x] = min(ary[b[i].x],b[i].y);}else if(b[i].typ==3){aly[b[i].x-1] = max(aly[b[i].x-1],k-b[i].y);}}bool ok = true;for(int i=0; i<=mx; i++) {if(alx[i]>arx[i]||aly[i]>ary[i]) {ok = false; break;}}if(!ok) continue;NetFlow::init();for(int i=0; i<=mx; i++){NetFlow::addedge(i==0?1:i+2,i+3,alx[i],arx[i],0ll);}for(int i=0; i<=mx; i++){NetFlow::addedge(i+mx+4,i==mx?2:i+mx+5,aly[i],ary[i],0ll);}for(int i=1; i<=n; i++){NetFlow::addedge(a[i].x+3,a[i].y+mx+4,0,1,a[i].w);}llong cur = NetFlow::flow(mx+mx+4,1,2,k);ans = max(ans,cur);}printf("%lld\n",ans);return 0;
}

这篇关于AtCoder AGC031E Snuke the Phantom Thief (费用流)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/711788

相关文章

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

cf 164 C 费用流

给你n个任务,k个机器,n个任务的起始时间,持续时间,完成任务的获利 每个机器可以完成任何一项任务,但是同一时刻只能完成一项任务,一旦某台机器在完成某项任务时,直到任务结束,这台机器都不能去做其他任务 最后问你当获利最大时,应该安排那些机器工作,即输出方案 具体建图方法: 新建源汇S T‘ 对任务按照起始时间s按升序排序 拆点: u 向 u'连一条边 容量为 1 费用为 -c,

AtCoder Beginner Contest 370 Solution

A void solve() {int a, b;qr(a, b);if(a + b != 1) cout << "Invalid\n";else Yes(a);} B 模拟 void solve() {qr(n);int x = 1;FOR(i, n) FOR(j, i) qr(a[i][j]);FOR(i, n) x = x >= i ? a[x][i]: a[i][x];pr2(

AtCoder Beginner Contest 369 D - Bonus EXP 动态规划

原题链接: https://atcoder.jp/contests/abc369/tasks/abc369_d 思路:   这道题为什么要用动态规划呢,其实,对于第i个怪物,我们有打与不打两种处理方式,而对于打,我们是获得两倍的经验值,还是一倍的经验值,与我们打了奇数只怪物还是打了偶数只怪物有关了,因此我们定义dp[i][0] 为前i只怪物总共打了偶数次,dp[i][1] 为前i只怪物总

【HDU】 4067 Random Maze 费用流

Random Maze Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1114    Accepted Submission(s): 387 Problem Description In the game “A C