给定一个由 n 行数字组成的数字梯形如下图所示。梯形的第一行有 m 个数字。从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
分别遵守以下规则:
1.从梯形的顶至底的 m 条路径互不相交;
2.从梯形的顶至底的 m 条路径仅在数字结点处相交;
3.从梯形的顶至底的 m 条路径允许在数字结点相交或边相交。
这道题是个费用流,刚开始在考虑路径互不相交的时候像了那种X型相交怎么办。。然后突然发现。。那种情况根本不可能发生。所以每个点只能经过一次,我们可以把点拆开,容量限制1,就是每个点只能经过一次。流出M条,容量1。流入M条,容量。上下不同两点间最多经过1次。
第二种情况。点可以多次经过,所以点的容量限制是INF(或者不拆点)。
上下走的时候一条路线也只能走一次,不然路径就相交了。注意最后的汇点是INF(因为最后一行其实不用走,走到就流入了)。
第三种情况路径可以相交,路径INF。//注意反向弧别改成INF。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
const int base=1e5;
const int INF=1e8;
struct edge{
int u,to,next,w,c;
}e[MAXN*40];
int head[MAXN],cnt=1;
inline void add(int u,int v,int w,int cost){e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;}
int n,m,s,t;
queue<int>q;
bool vis[MAXN];
int dis[MAXN],pre[MAXN];//mistake 1:开成了bool找不出来
bool SPFA(int x){
memset(pre,0,sizeof(pre));
for(int i=s;i<=t;i++)dis[i]=-INF;
q.push(x);dis[x]=0;vis[x]=1;
while(q.size()){
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].c;
if(e[i].w){
if(dis[u]+w>dis[v]){
dis[v]=dis[u]+w;pre[v]=i;
if(!vis[v]){
q.push(v);vis[v]=1;
}
}
}
}
}
if(dis[t]==-INF)return 0;
return 1;
}
int work(){
int cost=0;
while(SPFA(s)){
int tem=INF;
for(int i=pre[t];i;i=pre[e[i].u]){
tem=min(tem,e[i].w);
}
for(int i=pre[t];i;i=pre[e[i].u]){
e[i].w-=tem;e[i^1].w+=tem;
cost+=tem*e[i].c;
}
}
return cost;
}
int cun[25][25];
void readd1(){
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
for(int i=1;i<=m;i++){
for(int j=1;j<n+i;j++){
int temss=(n+(n+i-3))*(i-2)/2;//到上上行有多少个点
int tems=(n+(n+i-2))*(i-1)/2;
// cout<<temss<<" "<<tems<<endl;
if(i==1){
add(s,j,1,0),add(j,s,0,0);
add(j,j+base,1,cun[i][j]),add(j+base,j,0,-cun[i][j]);
}
else if(i==m){
// printf("tems+j:%d temss+j+base:%d\n",tems+j,temss+j+base);
// printf("temss+j-1+base:%d\n",temss+j-1+base,tems+j);
if(j!=n+i-1)add(temss+j+base,tems+j,1,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,1,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,1,cun[i][j]),add(tems+j+base,tems+j,0,-cun[i][j]);
add(tems+j+base,t,1,0),add(t,tems+j+base,0,0);
}
else {
// printf("tems+j:%d temss+j+base:%d\n",tems+j,temss+j+base);
// printf("temss+j-1+base:%d",temss+j-1+base,tems+j);
if(j!=n+i-1)add(temss+j+base,tems+j,1,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,1,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,1,cun[i][j]),add(tems+j+base,tems+j,0,-cun[i][j]);
//mistake 2:忘了判断上面那两个条件
}
}
}
printf("%d\n",work());
}
void readd2(){
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
for(int i=1;i<=m;i++){
for(int j=1;j<n+i;j++){
int temss=(n+(n+i-3))*(i-2)/2;
int tems=(n+(n+i-2))*(i-1)/2;
if(i==1){
add(s,j,1,0),add(j,s,0,0);
add(j,j+base,INF,cun[i][j]),add(j+base,j,0,-cun[i][j]);
}
else if(i==m){
if(j!=n+i-1)add(temss+j+base,tems+j,1,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,1,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,INF,cun[i][j]),add(tems+j+base,tems+j,0,-cun[i][j]);
add(tems+j+base,t,INF,0),add(t,tems+j+base,0,0);
}
else {
if(j!=n+i-1)add(temss+j+base,tems+j,1,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,1,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,INF,cun[i][j]),add(tems+j+base,tems+j,0,-cun[i][j]);
}
}
}
printf("%d\n",work());
}
void readd3(){
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
for(int i=1;i<=m;i++){
for(int j=1;j<n+i;j++){
int temss=(n+(n+i-3))*(i-2)/2;
int tems=(n+(n+i-2))*(i-1)/2;
if(i==1){
add(s,j,1,0),add(j,s,0,0);
add(j,j+base,INF,cun[i][j]),add(j+base,j,INF,-cun[i][j]);
}
else if(i==m){
if(j!=n+i-1)add(temss+j+base,tems+j,INF,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,INF,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,INF,cun[i][j]),add(tems+j+base,tems+j,INF,-cun[i][j]);
add(tems+j+base,t,INF,0),add(t,tems+j+base,0,0);
}
else {
if(j!=n+i-1)add(temss+j+base,tems+j,INF,0),add(tems+j,temss+j+base,0,0);
if(j>1)add(temss+j-1+base,tems+j,INF,0),add(tems+j,temss+j-1+base,0,0);
add(tems+j,tems+j+base,INF,cun[i][j]),add(tems+j+base,tems+j,INF,-cun[i][j]);
}
}
}
printf("%d\n",work());
}
int main(){
memset(cun,0,sizeof(cun));
scanf("%d%d",&n,&m);
s=0;t=(n+(n+m-1))*m/2+1+base;
for(int i=1;i<=m;i++){
for(int j=1;j<n+i;j++){
scanf("%d",&cun[i][j]);
}
}
readd1();
readd2();
readd3();
return 0;
}