链接:http://acm.hdu.edu.cn/showproblem.php?pid=4862
题意:有一个n*m的网格,每一个格子里有一个0到9的数字。你的初始能量是0。你最多能玩K次游戏,
每次游戏开始时你可以选择任意一个之前为走过的网格作为起点。然后,你可以选择在当前网格下,
向右或者向下跳跃至之前为到达过的网格。每次游戏过程中,你可以跳跃任意你想跳跃的次数,
只要没有违反前述规则。如果你从网格(x1,y1)跳跃至网格(x2,y2),那么你就将消耗|x1-x2|+|y1-y2|-1的能量。
能量值可以为负值,然而,在一次跳跃过程中,如果你起点的数字和终点的数字都是相同的S,
那么你将增长S能力值。求你能获得的最大的能量值。注意你必须到达每一个网格,但你并不需要玩完K次游戏。
思路:最小K路径覆盖。额,,,直接用费用流写的。
【以下转自杭电官方解题报告】
最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,
流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,
那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,
源点向其连边,费用0,流量K,这个点向Y部每个点连边,费用0,流量1,最这个图跑最小费用最大流,如果满流就是存在解,
反之不存在,最小费用的相反数就是可以获得的最大能量
关于加粗的那句话:这个表示从任意节点出发K次的构图方式学习了。
另外,因为每个点都要走遍,所以最终满流才表示存在解,否则无解。
二分图的构造方式,还是比较常见的。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#define MAX 250
#define INF 0x7fffffff
using namespace std;
struct Point
{
int x,y,val;
}p[MAX];
struct Edge
{
int to;
int cap;
int cost;
int next;
}edge[400000];
int n,m,k;
int st,ed,K_virtual;
int cnt,head[MAX];
int dist[MAX],vist[MAX],pre[MAX],pos[MAX];
void add(int u,int v,int cap,int cost)
{
edge[cnt].to=v;
edge[cnt].cap=cap;
edge[cnt].cost=cost;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].to=u;
edge[cnt].cap=0;
edge[cnt].cost=-cost;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void input()
{
int num=0;
char str[15];
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",str);
for(int j=0;j<m;j++)
{
p[++num].x=i;
p[num].y=j+1;
p[num].val=str[j]-'0';
}
}
cnt=0;
memset(head,-1,sizeof(head));
st=0;
ed=2*n*m+1+1;
K_virtual=2*n*m+1; //构造任意出发点
add(st,K_virtual,k,0);
for(int i=1;i<=num;i++)
{
add(K_virtual,i+num,1,0); //模拟任意起始点
add(st,i,1,0);
add(i+num,ed,1,0);
for(int j=1;j<=num;j++)
{
if((p[i].x==p[j].x&&p[i].y<p[j].y)||(p[i].y==p[j].y&&p[i].x<p[j].x))//能一部走到的,连边,权值为消耗的能量减去得到的能量
{
int energy=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y)-1;
if(p[i].val==p[j].val)
{
energy=energy-p[i].val;
}
add(i,j+num,1,energy);
}
}
}
}
void mcmf()
{
int u,maxflow=0,mincost=0;
for(;;)
{
for(int i=st;i<=ed;i++)
{
vist[i]=0;
pre[i]=-1;
dist[i]=INF;
}
dist[st]=0;
vist[st]=1;
pre[st]=st;
queue<int>q;
q.push(st);
while(!q.empty())
{
u=q.front();
q.pop();
vist[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].cap>0&&dist[v]>dist[u]+edge[i].cost)
{
dist[v]=dist[u]+edge[i].cost;
pre[v]=u;
pos[v]=i;
if(!vist[v])
{
vist[v]=1;
q.push(v);
}
}
}
}
if(dist[ed]==INF)break;
maxflow++;
mincost+=dist[ed];
for(u=ed;u!=st;u=pre[u])
{
edge[pos[u]].cap--;
edge[pos[u]^1].cap++;
}
}
if(maxflow==n*m) //满流才有解
printf("%d\n",-mincost);
else
printf("-1\n");
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
input();
printf("Case %d : ",i);
mcmf();
}
return 0;
}