洛谷P4013 数字梯形问题(费用流)
两个感受:码量感人……大佬nb……
规则一:$m$条路径都不相交,那么每一个点只能经过一次,那么考虑拆点,把每一个点拆成$A_{i,j}$和$B_{i,j}$,然后两点之间连一条容量$1$,费用该点本身数值的边,表明这个点只能被选一次,然后每一个点的$B$向它能到达的点的$A$连边,表明能从这个点到另一个点,容量随意,费用$0$,然后源点向第一排所有点的$A$连边,最后一排所有点的$B$向汇点连边,都是容量随意,费用$0$,然后跑一个最大费用流即可
规则二:每一个点可以被选多次,那么不用拆点了,直接每一个点向它能到的点连边,容量$1$,表明一条边只能被选一次,费用为该点的数值,源点向第一排所有点连边,容量$1$,费用$0$,最后一排所有点向汇点连边,费用为该点的数值,然后跑一个最大费用流即可
规则三:把每一条边只能选一次的限制去掉,总之就是除了源点到第一排的边,其他边的容量都改为$inf$,然后跑一个最大费用流
1 //minamoto 2 #include<bits/stdc++.h> 3 #define inf 0x3f3f3f3f 4 using namespace std; 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 6 char buf[1<<21],*p1=buf,*p2=buf; 7 inline int read(){ 8 #define num ch-'0' 9 char ch;bool flag=0;int res; 10 while(!isdigit(ch=getc())) 11 (ch=='-')&&(flag=true); 12 for(res=num;isdigit(ch=getc());res=res*10+num); 13 (flag)&&(res=-res); 14 #undef num 15 return res; 16 } 17 const int N=10005,M=50005; 18 int ver[M],head[N],flow[M],edge[M],Next[M],tot=1; 19 int vis[N],dis[N],disf[N],Pre[N],last[N],s=0,t=10000; 20 int a[25][55],b[25][55]; 21 queue<int> q; 22 inline void add(int u,int v,int f,int e){ 23 ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e; 24 ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=0,edge[tot]=-e; 25 } 26 bool spfa(){ 27 memset(dis,0xef,sizeof(dis)); 28 q.push(s),dis[s]=0,disf[s]=inf,Pre[t]=-1; 29 while(!q.empty()){ 30 int u=q.front();q.pop();vis[u]=0; 31 for(int i=head[u];i;i=Next[i]){ 32 int v=ver[i]; 33 if(flow[i]&&dis[v]<dis[u]+edge[i]){ 34 dis[v]=dis[u]+edge[i],last[v]=i,Pre[v]=u; 35 disf[v]=min(disf[u],flow[i]); 36 if(!vis[v]) vis[v]=1,q.push(v); 37 } 38 } 39 } 40 return ~Pre[t]; 41 } 42 int dinic(){ 43 int maxcost=0; 44 while(spfa()){ 45 int u=t; 46 maxcost+=disf[t]*dis[t]; 47 while(u!=s){ 48 flow[last[u]]-=disf[t]; 49 flow[last[u]^1]+=disf[t]; 50 u=Pre[u]; 51 } 52 } 53 return maxcost; 54 } 55 void clear(){ 56 tot=1,memset(head,0,sizeof(head)); 57 } 58 int main(){ 59 int n,m,k,o,num=0; 60 k=m=read(),n=read(); 61 o=((m<<1)+n-1)*n>>1; 62 for(int i=1;i<=n;++i,++k) 63 for(int j=1;j<=k;++j) 64 a[i][j]=read(),b[i][j]=++num; 65 k=m; 66 for(int i=1;i<=k;++i) 67 add(s,b[1][i],1,0); 68 for(int i=1;i<n;++i,++k) 69 for(int j=1;j<=k;++j){ 70 add(b[i][j],b[i][j]+o,1,a[i][j]); 71 add(b[i][j]+o,b[i+1][j],1,0); 72 add(b[i][j]+o,b[i+1][j+1],1,0); 73 } 74 for(int i=1;i<=k;++i){ 75 add(b[n][i],b[n][i]+o,1,a[n][i]); 76 add(b[n][i]+o,t,1,0); 77 } 78 printf("%d\n",dinic()); 79 clear(); 80 k=m; 81 for(int i=1;i<=k;++i) 82 add(s,b[1][i],1,0); 83 for(int i=1;i<n;++i,++k) 84 for(int j=1;j<=k;++j){ 85 add(b[i][j],b[i+1][j],1,a[i][j]); 86 add(b[i][j],b[i+1][j+1],1,a[i][j]); 87 } 88 for(int i=1;i<=k;++i) 89 add(b[n][i],t,inf,a[n][i]); 90 printf("%d\n",dinic()); 91 clear(); 92 k=m; 93 for(int i=1;i<=m;++i) add(s,b[1][i],1,0); 94 for(int i=1;i<n;++i,++k) 95 for(int j=1;j<=k;++j){ 96 add(b[i][j],b[i+1][j],inf,a[i][j]); 97 add(b[i][j],b[i+1][j+1],inf,a[i][j]); 98 } 99 for(int i=1;i<=k;++i) add(b[n][i],t,inf,a[n][i]); 100 printf("%d\n",dinic()); 101 return 0; 102 }
深深地明白自己的弱小