[HDU 4862][2014 Multi-University Training Contest 1]Jump

There are n*m grids, each grid contains a number, ranging from 0-9. Your initial energy is zero. You can play up to K times the game, every time you can choose any one of the grid as a starting point (but not traveled before) then you can choose a grid on the right or below the current grid to jump, but it has not traveled before. Every time you can jump as many times as you want, as long as you do not violate rules. If you are from (x1, y1) to (x2, y2), then you consume |x1-x2|+|y1-y2|-1 energies. Energy can be negative. 
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;
} 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值