今天本来是想做个动态规划的题,所以看了这道题,但是想了好久都没有思路,无奈下看了看讨论区,说是可以用最大费用最大流搞,于是鉴于昨天学习了最大流的基础上学习了一下最小费用(最大费用)最大流,像这类题对于初学者来说,建图是拦路虎,因此建好图是解决此题的关键,,
思路:由于在传纸条的过程中需要传一个来回,并且每个位置都用一次,如果直接建图,不好限制每个位置直走一次,所以这里用到了拆点,把每个点都拆成两个点,然后让每个点和其相邻的右边和下面的点相连,并且规定该边的容量为1,这就很好的控制了每个点只走一次,同时费用即0(好心程度),因为每个点都拆成两个点,自己和自己必然是联通的,这条边的容量也为1,费用为(该位置好心程度),对于源点和汇点因为需要走两次故其容量为2,因为求的是(最大费用)故上边所设的费用为0不影响,这样就可以求出从原点到汇点存在增光流时的最大费用。。。。
AC代码:
#include<iostream> #include<algorithm> #include<string.h> #include<queue> #include<cstdio> #define M 5005 #define N 210010 #define inf 0xfffff using namespace std; typedef struct str { int v; int w; int f; int next; }Edge; Edge po[N]; int head[M],pre[N],dis[M]; bool vis[M]; int tot,n,m; void init() { memset(head,-1,sizeof(head)); tot=0; } void add(int a,int b,int c,int d) { po[tot].v=b; po[tot].w=c; po[tot].f=d; po[tot].next=head[a]; head[a]=tot++; po[tot].v=a; po[tot].w=0; po[tot].f=-d; po[tot].next=head[b]; head[b]=tot++; } int Read() { int data=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); do { data=data*10+ch-'0'; ch=getchar(); }while(ch>='0'&&ch<='9'); return data; } bool SPFA(int s,int t) { queue<int>Q; memset(vis,false,sizeof(vis)); memset(pre,-1,sizeof(pre)); for(int i=0;i<=t;++i) dis[i]=-inf; dis[s]=0; Q.push(s); vis[s]=true; while(!Q.empty()) { int cur=Q.front(); Q.pop(); vis[cur]=false; for(int i=head[cur];i!=-1;i=po[i].next) { if(po[i].w>0&&dis[po[i].v]<dis[cur]+po[i].f) { dis[po[i].v]=dis[cur]+po[i].f; pre[po[i].v]=i;//记录前向边 if(!vis[po[i].v]) { vis[po[i].v]=true; Q.push(po[i].v); } } } } if(pre[t]==-1) return false;//在保证最大费用的情况下,判断能不能达到汇点 else return true; } void max_flow(int s,int t) { int ans=0; while(SPFA(s,t)) { ans+=dis[t];//更新最大费用 int now=pre[t]; while(now!=-1)//更新残留网络 { po[now].w--; po[now^1].w++; now=pre[po[now^1].v];//找前向边 } } printf("%d\n",ans); } int main() { int T=Read(); while(T--) { init(); m=Read(); n=Read(); add(0,1,2,0); add(2*(m*n-1),2*(m*n-1)+1,2,0); for(int i=0;i<m;++i) for(int j=0;j<n;++j) { int a=Read(); if(j!=n-1) add(2*(i*n+j)+1,2*(i*n+j+1),1,0); if(i!=m-1) add(2*(i*n+j)+1,2*((i+1)*n+j),1,0); if(!(i==0&&j==0)&&!(i==m-1&&j==n-1)) add(2*(i*n+j),2*(i*n+j)+1,1,a); } max_flow(0,2*(m*n-1)+1); }return 0; }