However, in a jump, if you start position and end position has same numbers S, then you can increase the energy value by S.
Give me the maximum energy you can get. Notice that you have to go each grid exactly once and you don’t have to play exactly K times.
【题目大意】
给一个NxM的棋盘,以及游戏次数k,每次游戏可以选取棋盘上任意一个格子,然后向右方或者下方的方格跳跃(不一定相邻),跳跃次数不限。每个格子都会给定一个能量数值x(x在0到9之间),若一次跳跃的起始方格和结束方格数字相等,则获得该能量x。另外每次跳跃会消耗能量,数值为起始与结束方格横坐标之差的绝对值与纵坐标之差的绝对值之和再减1,即|x1-x2|+|y1-y2|-1。问能否在k次游戏之内遍历整个棋盘,若能,求出最终可能的最大能量值。(注意,可以选取一个方格但不进行任何跳跃)
【解题思路】
将每次游戏的跳跃线路看成一条路径,则本题转化成带有路径数量限制的路径覆盖问题,可以使用最小费用流算法解决。
最小费用流算法思路:将棋盘上每个方格拆成两个点a和a’,引入源和汇,源对X部所有节点连容量为1,费用为0的边,Y部所有节点对汇连容量为1,费用为0的边。若棋盘上方格a可以一步到达方格b,则引入边a→b’,费用为该步消耗能量值减去获得能量值,容量为1。另外增加一个节点,连源到该点容量为k,费用为0的边,再连该点到Y部各点容量为1,费用为0的边(倒过来,连汇和X部也可以),代表可以有k条路径。对该图进行最小费用流算法,若流量为NxM则有解,解为费用的相反数,否则无解。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<deque>
#include<cmath>
#include<conio.h>
using namespace std;
struct par
{
int a, b;
par(int _a = 0, int _b = 0) : a(_a), b(_b) {}
};
int graph[12][12];
int test,lines,cols,lim;
//===========================NetworkCostFlowZkw========
//Zkw费用流在处理二分图方面效率较高
const int NCFZmaxn = 1000;
const int NCFZmaxm = 10005;
const int NCFZinf_ = 0x7f;
const int NCFZinf = 0x7f7f7f7f;
struct NCFZ_Line
{
int fr, to, next, v, c, opt;
};
struct Network_Cost_Flow_Zkw
{
NCFZ_Line li[NCFZmaxm];
int be[NCFZmaxn], l, s, t, dist[NCFZmaxn], b[NCFZmaxn];
deque<int> q;
void makeline(int fr, int to, int v, int c)
{
++l;
li[l].next = be[fr];
be[fr] = l;
li[l].fr = fr;
li[l].to = to;
li[l].v = v;
li[l].c = c;
li[l].opt = l + 1;
++l;
li[l].next = be[to];
be[to] = l;
li[l].fr = to;
li[l].to = fr;
li[l].v = 0;
li[l].c = -c;
li[l].opt = l - 1;
}
void create()
{
}
void clear()
{
l = s = t = 0;
memset(be, 0, sizeof(be));
memset(b, 0, sizeof(b));
}
bool spfa()
{
memset(dist, NCFZinf_, sizeof(dist));
memset(b, 0, sizeof(b));
dist[t] = 0;
b[t] = 1;
q.push_back(t);
while (!q.empty())
{
int now = q.front();
q.pop_front();
for (int i = be[now]; i; i = li[i].next)
{
int to = li[i].to;
if (!li[li[i].opt].v || dist[to] <= dist[now] - li[i].c) continue;
dist[to] = dist[now] - li[i].c;
if (!b[to])
{
b[to] = 1;
if (!q.empty() && dist[to] < dist[q.front()]) q.push_front(to);
else q.push_back(to);
}
}
b[now] = 0;
}
return dist[s] != NCFZinf;
}
int sap(int now, int maxf)
{
if (now == t) return maxf;
int tot = 0;
b[now] = 1;
for (int i = be[now]; i; i = li[i].next)
{
int to = li[i].to;
if (!b[to] && li[i].v && dist[to] == dist[now] - li[i].c)
{
int k = sap(to, min(maxf - tot, li[i].v));
li[i].v -= k;
li[li[i].opt].v += k;
tot += k;
}
}
return tot;
}
par query(int S, int T)
{
par ans;
ans.a = ans.b = 0;
s = S, t = T;
while (spfa())
while (int k = sap(s, NCFZinf))
{
memset(b, 0, sizeof(b));
ans.a += k;
ans.b += k * dist[s];
}
return ans;
}
};
Network_Cost_Flow_Zkw B;
int cost(int x1,int y1,int x2,int y2){
int ans=abs(x1-x2)+abs(y1-y2)-1;
if(graph[x1][y1]==graph[x2][y2])ans=ans-graph[x2][y2];
return ans;
}
int main(){
cin>>test;int num=0;
while(test--){
num++;
B.clear();
char chr;
scanf("%d%d%d",&lines,&cols,&lim);
int mul=lines*cols;
for(int i=0;i<lines;i++)
for(int j=0;j<cols;j++){
do{
chr=getchar();
}while(chr<48||chr>58);
graph[i][j]=chr-48;
B.makeline(0,i*cols+j+1,1,0);
B.makeline(mul+i*cols+j+1,2*mul+1,1,0);
B.makeline(2*mul+2,mul+i*cols+j+1,1,0);
}
for(int i=0;i<lines;i++)
for(int j=0;j<cols;j++){
for(int k=j+1;k<cols;k++){B.makeline(i*cols+j+1,mul+i*cols+k+1,1,cost(i,j,i,k));}
for(int k=i+1;k<lines;k++){B.makeline(i*cols+j+1,mul+k*cols+j+1,1,cost(i,j,k,j));}
}
B.makeline(0,2*mul+2,lim,0);
par ans;
ans=B.query(0,2*mul+1);
if(ans.a<mul)cout<<"Case "<<num<<" : "<<-1<<endl;
else cout<<"Case "<<num<<" : "<<-ans.b<<endl;
}
return 0;
}