hdu 4862 Jump(最大费用最大流,巧妙构图)

来源:互联网 发布:爱知学院大学排名 编辑:程序博客网 时间:2024/05/20 17:08

Jump

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1350    Accepted Submission(s): 576


Problem Description
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.
 

Input
The first line is an integer T, stands for the number of the text cases.
Then T cases followed and each case begin with three numbers N, M and K. Means there are N rows and M columns, you have K times to play.
Then N lines follow, each line is a string which is made up by M numbers.
The grids only contain numbers from 0 to 9.
(T<=100, N<=10,M<=10,K<=100)
 

Output
Each case, The first you should output “Case x : ”,(x starting at 1),then output The maximum number of energy value you can get. If you can’t reach every grid in no more than K times, just output -1.
 

Sample Input
51 5 1919291 5 2919291 5 3919293 3 33333333333 3 2333333333
 

Sample Output
Case 1 : 0Case 2 : 15Case 3 : 16Case 4 : 18Case 5 : -1
 
题意:给一个n*m的矩阵,现在问你能不能找出k条路径覆盖所有的点,路径里必须保证每一步都是向右走或者向左走(可以跳过多个格子),每一个格子只能走一次,如果能找到,输出最大的权值

权值的计算方法是,-起跳点和落地点的欧几里得距离,如果起跳点和落地点的点权一样,那么加上这个点权

思路:没能想出来,看了别人题解,构图真心巧妙啊..不过也好难想

把所有的点拆成两个点,一边连接超级源点s,一边连接超级汇点t,容量都为1,费用都为0

然后点之间能移动到的连一条边,容量为1,费用为题目给的权值计算方法算出来的权值

那么现在能联通到的点已经连通,但是发现没有?此时连的边如果画成一幅图,会出现一条或者多条路径,但是这些路径对于原图是没有起点的,(比如右边的(1,1)这个点永远不可能有入度)

所以我们从超级源点连出一条边到ss,容量为k,费用为0,用来表示k条路径的起点,因为这些边是没有费用的,所以想要得到最大费用必然会先去流上面的边,流完后才会考虑这里的边,再从ss连接n*m条边到右边的点,容量为1,费用为0即可

如果是满流则表示<=k条路径可以覆盖所有的点并且每个点只覆盖一次(这种情况下每个点的入度和出度都为1,肯定是在<=k条路径内)

否则表示k条路径不足以覆盖所有的点

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <queue>using namespace std;#define N 1050#define INF 999999999struct Edge{    int u,v,next,cap,cost;} edge[N*N];int cnt,head[N];char ma[55][55];int vis[N],pp[N],d[N],sumflow;void init(){    cnt=0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap,int cost){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt].cap=cap;    edge[cnt].cost=cost;    edge[cnt].next=head[u];    head[u]=cnt++;    edge[cnt].u=v;    edge[cnt].v=u;    edge[cnt].cap=0;    edge[cnt].cost=-cost;    edge[cnt].next=head[v];    head[v]=cnt++;}int spfa(int s,int t,int n){    queue<int>q;    memset(vis,0,sizeof(vis));    memset(pp,-1,sizeof(pp));///pp[i]表示最短路径上以i为终点的边的编号    for(int i=0; i<=n; i++)        d[i]=-INF;    d[s]=0;    vis[s]=1;    q.push(s);    while(!q.empty())    {        int u=q.front();        q.pop();        vis[u]=0;        for(int i=head[u]; i!=-1; i=edge[i].next)        {            int v=edge[i].v;            if(edge[i].cap>0&&d[v]<d[u]+edge[i].cost)            {                d[v]=d[u]+edge[i].cost;                pp[v]=i;                if(!vis[v])                {                    vis[v]=1;                    q.push(v);                }            }        }    }    if(d[t]==-INF) return 0;///找不到一条到终点的路    return 1;}int MCMF(int s,int t,int n){    int mincost=0,minflow,flow=0;///最大费用,路径中最小流量,总流量    while(spfa(s,t,n))///找当前的最长路    {        minflow=INF+1;        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])            minflow=min(minflow,edge[i].cap);///从路径中找最小的流量        flow+=minflow;///总流量加上最小流量        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])        {            edge[i].cap-=minflow;///当前边减去最小流量            edge[i^1].cap+=minflow;///反向边加上最小流量        }        mincost+=d[t]*minflow;///最小费用等于路径和*每条路径的流量(经过多少次)    }    sumflow=flow;    return mincost;}int main(){    int n,m,k,T,tot=1;    scanf("%d",&T);    while(T--)    {        init();        scanf("%d %d %d",&n,&m,&k);        for(int i=1; i<=n; i++)            scanf("%s",ma[i]+1);        int s=0,ss=2*n*m+1,t=2*n*m+2;        addedge(s,ss,k,0);        for(int i=1; i<=n*m; i++)            addedge(s,i,1,0);        for(int i=1; i<=n; i++)            for(int j=1; j<=m; j++)            {                int x=(i-1)*m+j;                for(int l=i+1; l<=n; l++)                {                    int y=(l-1)*m+j,q=-l+i+1;                    if(ma[i][j]==ma[l][j]) q+=ma[i][j]-'0';                    addedge(x,y+n*m,1,q);                }                for(int l=j+1; l<=m; l++)                {                    int y=(i-1)*m+l,q=-l+j+1;                    if(ma[i][j]==ma[i][l]) q+=ma[i][j]-'0';                    addedge(x,y+n*m,1,q);                }            }        for(int i=n*m+1; i<=2*n*m; i++)        {            addedge(ss,i,1,0);            addedge(i,t,1,0);        }        int ans=MCMF(s,t,t);        printf("Case %d : ",tot++);        if(sumflow<n*m) printf("-1\n");        else printf("%d\n",ans);    }    return 0;}





0 0