给一个n*m的矩阵,矩阵中每个位置有一个0~9的数字,每次选中一个点开始跳,只能向下或向右跳(不能既向下又向右跳)。每跳一次要消耗的能量是这两点的哈密顿距离-1,如果起跳点和着陆点的数字相同,就会得到与数字相同的能量。每次可以连续的跳。最多选取k个点开始跳,并且每个点都要跳到,每个点只能跳一次,求能量的最大值(初始能量为0)。
比赛时根本没看懂这题啥意思=。=
http://acm.hdu.edu.cn/showproblem.php?pid=4862
这题是最大费用最大流。
首先我们建一个源点,并把每个点拆成两个点i和i'。
从源点向每个i连一条容量为1,费用为0的边(每个点只能做一次起点,做起点不需要费用)
在建一个汇点,从每个i'向汇点连一条容量为1费用为0的边
如果可以从i跳向j,就建一条从i'到j的边,容量为1,费用为两点间的哈密顿距离-1(如果两点的数字相同,再减去这个数字)(因为是求最大费用,所以费用要加能量的负值)
从i向i'连一条容量为1,费用为-INF的边。因为流到这个点以后,一定要从这个点流出,再流到下一个点。这样在每个点的出点和入店间加一条费用为-INF的边,因为这条边的费用是最小的,所以会优先流过这个点。(注意:这里的INF不能和求最短路时的inf一样!QAQ)
那么如何保证不超过k次呢?
我们再建一个超级源点,从这个超级源点,向源点连一条容量为k费用为0的边,这样就保证的最多选k次起点。
但是!题目中还说,不一定要跳满k次,只要能量最大。
所以在求得最小费用的减小时,就退出。这样就保证了能量最大。
接下来就是如何判断是否每个点都已经跳过了。
因为每个点的入点和出点之间有一条费用为-INF的边,一共n*m个点就相当于费用多了-n*m*INF,所以就要看-mincost - m*n*INF是否小于0。
也就是-mincost / INF < m*n是否成立,如果成立就说明没有遍历所有的点输出-1.
否则答案应该是-mincost - m*n*INF,即-mincost % INF.
//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef double DB;
typedef long long ll;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const DB eps = 1e-6;
const int inf = ~0U>>1;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const int maxn = 1000 + 10;
/*mincost 为最小费用。若要求最大费用,加边时将费用取反,最后-mincost是最大费用*/
///先调用init,然后while(Find());
const int maxv = 1000 + 10;///顶点数
const int maxe = 1000000 + 10;///边数
struct node{
int v, w, cap, next; ///cap = 容量, w = 费用
}edge[maxe];
struct fnode{
int e, cap, w;///上一条边,容量,费用
}fn[maxv];
int head[maxv], cnt, st, ed, maxflow, mincost;
bool vis[maxv];
void addedge(int u, int v, int cap, int w){
edge[cnt].v = v; edge[cnt].w = w; edge[cnt].cap = cap; edge[cnt].next = head[u]; head[u] = cnt++;
edge[cnt].v = u; edge[cnt].w = -w; edge[cnt].cap = 0; edge[cnt].next = head[v]; head[v] = cnt++;
// printf("u:%d, v:%d, cap:%d, w:%d, cnt:%d\n", u, v, cap, w, cnt - 1);
}
bool Find(){///找最短路
memset(vis, false, sizeof(vis));
for(int i=st; i<=ed; i++) fn[i].w = inf;
fn[st].w = 0;
vis[st] = 1; fn[st].cap = inf;
queue<int> Q;
Q.push(st);
while(!Q.empty()){
int now = Q.front(); Q.pop();
vis[now] = 0;
for(int i=head[now]; ~i; i=edge[i].next){
int k = edge[i].v;
if(edge[i].cap && fn[now].w + edge[i].w < fn[k].w){
fn[k].w = fn[now].w + edge[i].w;
fn[k].cap = min(fn[now].cap, edge[i].cap);
fn[k].e = i;
if(!vis[k]){
vis[k] = 1; Q.push(k);
}
}
}
}
if(fn[ed].w == inf) return 0;
int tmp = mincost + fn[ed].cap * fn[ed].w;
if(tmp > mincost) return 0;//这里就是判断最小费用是不是在增加
mincost += fn[ed].cap * fn[ed].w;
maxflow += fn[ed].cap;
int i = ed;
while(i != st){
edge[fn[i].e].cap -= fn[ed].cap;
edge[fn[i].e ^ 1].cap += fn[ed].cap;
i = edge[fn[i].e ^ 1].v;
}
return 1;
}
void init(int source, int sink){
memset(head, -1, sizeof(head)); cnt = 0;
st = source; ed = sink;///源点和汇点
maxflow = mincost = 0;///最大流和最小费用
}
int T, n, m, k, a[maxn][maxn];
char str[maxn];
int main(){
scanf("%d", &T);
for(int cas=1; cas<=T; cas++){
scanf("%d%d%d", &n, &m, &k);
init(0, 2 * n * m + 2);
addedge(st, 1, k, 0);
for(int i=0; i<n; i++){
scanf("%s", str);
for(int j=0; j<m; j++){
a[i][j] = str[j] - '0';
addedge(1, i * m + j + 2, 1, 0);
addedge(m * n + i * m + j + 2, ed, 1, 0);
addedge(i * m + j + 2, m * n + i * m + j + 2, 1, -100000);
}
}
for(int i=0; i<n; i++)
for(int j=0; j<m; j++){
for(int k=i+1; k<n; k++){
int tmp = k - i - 1;
if(a[i][j] == a[k][j]) tmp -= a[i][j];
addedge(n * m + i * m + j + 2, k * m + j + 2, 1, tmp);
}
for(int k=j+1; k<m; k++){
int tmp = k - j - 1;
if(a[i][j] == a[i][k]) tmp -= a[i][j];
addedge(n * m + i * m + j + 2, i * m + k + 2, 1, tmp);
}
}
while(Find());
printf("Case %d : ", cas);
if(-mincost / 100000 < n * m) puts("-1");
else printf("%d\n", -mincost % 100000);
}
return 0;
}