POJ 3422 Kaka's Matrix Travels

来源:互联网 发布:方向余弦矩阵正交证明 编辑:程序博客网 时间:2024/05/24 16:16

费用流问题,只不过这次是最大费用,建边的时候权值取相反数,按最小费用流求完之后在输出负值就行。


题目大意:

Kaka有一个矩阵表格,行从1到n,列从1到n,Kaka从(1,1)开始,向(n,n)走,每一个格子里都有钱w,只能向下或向右走,走到那个格子就可以拿到那个格子里的钱。问走k次最多能拿多少钱。


这个题建图的时候需要拆点。每个格子都是一个点。把一个点拆成两个,两个点之间有两条路,一个容量为1,权值为那个格子的金钱数。另一条路容量为k-1,权值为0。因为走了一次钱捡起来之后就没钱了。


我的代码中为了处理方便,把超级原点设为了n*n*2,超级汇点设为了n*n*2+1。请大家在阅读中注意。


下面是代码:

#include <stdio.h>#include <string.h>#include <queue>using namespace std;const int inf=1<<30,M=5005;int n,m,map1[55][55],head[M],cnt,pre[M],dis[M];bool vis[M];struct node{    int u,v,w,f,next;} edge[100005];int min(int a,int b){    if(a>b)    {        a=b;    }    return a;}void add(int u,int v,int w,int f){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt].w=w;//正向边权值为正    edge[cnt].f=f;//正向边流量为f    edge[cnt].next=head[u];    head[u]=cnt;    cnt++;    edge[cnt].u=v;    edge[cnt].v=u;    edge[cnt].w=-w;//正向边权值为负    edge[cnt].f=0;//流量为零    edge[cnt].next=head[v];    head[v]=cnt;    cnt++;}bool spfa() //基于邻接表的SPFA算法{    int i;    for(i=0; i<=n*n*2+1; i++)  //初始化    {        pre[i]=-1;        dis[i]=inf;        vis[i]=false;    }    queue <int > q;    dis[n*n*2]=0;    vis[n*n*2]=true;    q.push(n*n*2);    while(!q.empty())    {        int t=q.front();        q.pop();        i=head[t];        vis[t]=false;        while(i!=-1)        {            if(edge[i].f>0&&dis[edge[i].v]>dis[t]+edge[i].w)            {                dis[edge[i].v]=dis[t]+edge[i].w;                pre[edge[i].v]=i;                if(!vis[edge[i].v])                {                    vis[edge[i].v]=true;                    q.push(edge[i].v);                }            }            i=edge[i].next;        }    }    if(pre[n*n*2+1]==-1)//如果不在最短路中   代表着最短路寻找失败    {        return false;    }    return true;}int main(){    while(scanf("%d%d",&n,&m)!=EOF)    {        int i,j;        cnt=0;        for(i=1; i<=n; i++)        {            for(j=1; j<=n; j++)            {                scanf("%d",&map1[i][j]);            }        }        memset(head,-1,sizeof(head));        for(i=1; i<=n; i++)   //每一点的拆点建边操作        {            for(j=1; j<=n; j++)            {                int u=(i-1)*n+j-1;                add(u*2,u*2+1,-map1[i][j],1);  //权值为负值                add(u*2,u*2+1,0,m-1);            }        }        for(i=1; i<=n; i++)//向下移动建边        {            for(j=1; j<n; j++)            {                int u=(i-1)*n+j-1;                add(u*2+1,(u+1)*2,0,m);            }        }        for(i=1; i<n; i++)//向右移动建边        {            for(j=1; j<=n; j++)            {                int u=(i-1)*n+j-1;                add(u*2+1,(u+n)*2,0,m);            }        }        add(n*n*2,0,0,m);//超级原点建边        add(n*n*2-1,n*n*2+1,0,m);//超级汇点建边        int ans=0;        while(spfa())  //如果最短增广路寻找成功        {            int max1=inf;            int p=pre[n*n*2+1];//初始化P指针            while(p!=-1) //寻找关键流量            {                max1=min(max1,edge[p].f);                p=pre[edge[p].u];            }            p=pre[n*n*2+1];            while(p!=-1)  //修改流量            {                edge[p].f-=max1;                edge[p^1].f+=max1;                ans+=max1*edge[p].w;                p=pre[edge[p].u];            }        }        printf("%d\n",-ans);    }    return  0;}




原创粉丝点击