题意:给一个矩阵,可以选择任意的一个点,作为起点,上面的人可以向下或者向右跳,他的花费是|x1-x2|+|y1-y2|-1,如果他跳一步的起点和终点上的数字一样的,那么他就可以赚到数字的价格。以上叙述是一轮游戏,现在最多可以进行k轮游戏,有一个条件,每一个格子只能走一次,而且每一个格子都要走到。问完成游戏最多可以赚多少?
想法:有两个关键点,每个格子必须走,且走一次,还有一点就是最多k轮游戏。
费用流解决:
1.虚拟source,fucksource,sink
2.source向fucksource连一条容量为k费用为0的边
解释:游戏最多进行k轮这里的边,表示正好k轮游戏。
3.fucksource向sink连一条容量为inf费用为0的边
解释:因为有可能不需要k轮游戏,就可以解决战斗。
4.a点拆成a,a',a向a'连一条容量为1,费用为inf的边
解释:应为每一个格子必须要走,容量为1是为了限制每一个格子只能走一次,对于这里的费用为什么要设置为inf,接着往下看
5.fucksource向a连一条容量为1,费用为0的边
解释:选择一个起点,每一个点都可能是起点,所以连接所有点
6.a'向sink连一条容量为1,费用为0的边
解释:选择一个终点,每一个点都可能是终点,所以连接所有点
7.a'向b(另外一个点,拆点之后的)连一条容量为1,费用为所求的的边
解释:这个人可以从a跳到哪里,所有可能的点都连上
其实网络流就相当于图的暴力。下面解释为什么我的把a->a'的费用置为inf,首先明确,要找最长路,每个点必须找到,因为拆点只能保证这个点只能被用一次或者不用,但是我们并不可以确定到底是用了还是没有用,当把费用置为inf时,如果这个点被选择那么一条最长路上必定会有这一个inf值,那么最后取到的最大费用里面,如果满足题目要求那么必定会有n*m个点的inf被加了进去,所以如果最大费用-n*m*inf,如果它的值小于0表示有的点并没有走到,如果大于0表示这就是最后答案。
提示:每一次找到一条最长路它的值去掉对应的inf它的值一定大于等于0,因为,当你一次跳一个格子的时候,你是不会产生花费的。
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #define inf 10000 using namespace std; const int nodes=200+5; const int edges=5000; int n,m,k,s,ss,t,totfee; char map[15][15]; struct node { int u,v,next; int flow,fee; }e[edges]; int head[nodes],cur[nodes],cnt; class Dinic { public: int spfa() { queue<int>q; while(!q.empty()) q.pop(); for(int i=s;i<=t;i++) { dis[i]=-inf; } memset(vis,0,sizeof(vis)); memset(pe,-1,sizeof(pe)); vis[s]=1; dis[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i+1;i=e[i].next) { int v=e[i].v; if(dis[v]<dis[u]+e[i].fee&&e[i].flow>0) { dis[v]=dis[u]+e[i].fee; pe[v]=i; if(!vis[v]) { vis[v]=1; q.push(v); } } } } return dis[t]!=-inf; } int Min(int a,int b) { if(a<b) return a; return b; } int dfs(int u,int flow) { int cost=0; if(u==t) { totfee+=dis[t]; return flow; } for(int &i=cur[u];i+1;i=e[i].next) { int v=e[i].v; if(pe[v]==i&&e[i].flow>0) { int minn=dfs(v,Min(e[i].flow,flow-cost)); if(minn>0) { e[i].flow-=minn; e[i^1].flow+=minn; cost+=minn; if(cost==flow) break; } else pe[v]=-1; } } return cost; } void result() { while(spfa()) { for(int i=s;i<=t;i++) cur[i]=head[i]; int pp=dfs(s,inf); } } private: int dis[nodes],vis[nodes],pe[nodes]; }dinic; void Init() { memset(head,-1,sizeof(head)); cnt=0; } void add(int a,int b,int flow,int fee) { e[cnt].v=b; e[cnt].flow=flow; e[cnt].fee=fee; e[cnt].next=head[a]; head[a]=cnt++; e[cnt].v=a; e[cnt].flow=0; e[cnt].fee=-fee; e[cnt].next=head[b]; head[b]=cnt++; } int aabs(int x) { if(x>0) return x; return -x; } void build_map() { s=0;ss=2*n*m+1;t=2*n*m+2; add(s,ss,k,0); add(ss,t,inf,0); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int num=(i-1)*m+j; add(ss,num,1,0); add(num+n*m,t,1,0); add(num,num+n*m,1,inf); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int u=(i-1)*m+j; for(int l=j+1;l<=m;l++) { int v=(i-1)*m+l; int dis=aabs(j-l)-1; if(map[i][j]!=map[i][l]) add(u+m*n,v,1,0-dis); else { int w=map[i][j]-'0'-dis; add(u+n*m,v,1,w); } } for(int l=i+1;l<=n;l++) { int v=(l-1)*m+j; int dis=aabs(l-i)-1; if(map[i][j]!=map[l][j]) add(u+m*n,v,1,0-dis); else { int w=map[i][j]-'0'-dis; add(u+n*m,v,1,w); } } } } } void treatment(int ca) { totfee=0; dinic.result(); int ans=totfee-n*m*inf; printf("Case %d : ",ca); if(ans<0) printf("-1\n"); else printf("%d\n",ans); } int main() { int test,ca=1; scanf("%d",&test); while(test--) { Init(); scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;i++) scanf("%s",map[i]+1); build_map(); treatment(ca++); } return 0; }