LightOJ

来源:互联网 发布:淘宝云标签入口 编辑:程序博客网 时间:2024/06/08 10:07

原题地址:点击打开链接

题意:给你一个矩阵,每个点上都有一个数字代表需要被救的人数,你从(1,1)点出发开始救人走过的点不能再走,不过你只能向右或者向下走,到达终点后(m,n)返回起点,只能向上或者左走,每路过一个点就可以救该点有的人数,问你最多可以就多少人。

思路:可转化为最小费用流问题,关键的难点在于建图,该题需要拆点,每个点到自身的流量为1,(起点和终点的流量为2),然后根据顶点之间的可达性进行建边,最后注意结果要将起点和终点的人数减去一次,因为多加了一次。

#include<stdio.h>#include<string.h>#include<vector>#define INF 1<<31-1#define min(x,y)(x<y?x:y)using namespace std;struct Edge{int to;int cap;int rev;int cost;};vector<Edge>g[20010]; void add_edge(int from,int to,int cap,int cost){g[from].push_back((Edge){to,cap,g[to].size(),cost});g[to].push_back((Edge){from,0,g[from].size()-1,-cost});}int id[110][110];int prev[20010];int pree[20010];int dis[20010];int min_cost_flow(int s,int t,int f){int res=0,i,u,v;bool update;while(f>0){update=true;for(i=s;i<=t;i++)dis[i]=-INF;dis[s]=0;while(update){update=false;for(u=1;u<=t;u++){if(dis[u]==-INF)continue;for(i=0;i<g[u].size();i++){Edge e=g[u][i];v=e.to;if(e.cap>0&&dis[v]<dis[u]+e.cost){prev[v]=u;pree[v]=i;dis[v]=dis[u]+e.cost;update=true;}}}}if(dis[t]==-INF)break;for(u=t;u!=s;u=prev[u]){Edge &e=g[prev[u]][pree[u]];e.cap-=1;g[e.to][e.rev].cap+=1;}res+=dis[t];f-=1;}return res;}int main(){int i,j,c,m,n,V,cost,num,u,v,s,t,k=0,x;scanf("%d",&c);while(c--){num=0;x=0;scanf("%d%d",&m,&n);V=m*n;s=1;t=V*2;for(i=s;i<=t;i++)g[i].clear();for(i=1;i<=m;i++)for(j=1;j<=n;j++){id[i][j]=++num;scanf("%d",&cost);if(num==1){add_edge(num,num+V,2,cost);x+=cost;}else if(num==V){x+=cost;add_edge(num,num+V,2,cost);}elseadd_edge(num,num+V,1,cost);}for(i=1;i<=m;i++)for(j=1;j<=n;j++){int a=i+1;int b=j;if(a>=1&&a<=m&&b>=1&&b<=n){u=id[i][j];v=id[a][b];add_edge(u+V,v,1,0);}a=i;b=j+1;if(a>=1&&a<=m&&b>=1&&b<=n){u=id[i][j];v=id[a][b];add_edge(u+V,v,1,0);}}int res=min_cost_flow(s,t,2);printf("Case %d: %d\n",++k,res-x);}return 0;} /*23 31 1 11 0 11 1 13 41 1 0 11 1 1 10 1 10 1*/


0 0
原创粉丝点击