三取方格数

三取方格数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

设有N*N的方格图,我们将其中的某些方格填入正整数,
而其他的方格中放入0。
某人从图得左上角出发,可以向下走,也可以向右走,直到到达右下角。
在走过的路上,他取走了方格中的数。(取走后方格中数字变为0)
此人从左上角到右下角共走3次,试找出3条路径,使得取得的数总和最大。

 

输入

第一行:N (4<=N<=20)
接下来一个N*N的矩阵,矩阵中每个元素不超过80,不小于0

 

输出

一行,表示最大的总和。

 

样例输入

4
1 2 3 4
2 1 3 4
1 2 3 4
1 3 2 4

样例输出

39
题解:
法一:
DP
f[x1][y1][x2][x3]第一个人走到(x1,y1),第二个人走到(x2,x1+y1-x2),第三个人走到(x3,x1+y1-x3)总和最大值 

(x1-1,y1)->(x1,y1)向右 (x2,x1-1+y1-x2)->(x2,x1+y1-x2)向下 (x3,x1-1+y1-x3)->(x3,x1+y1-x3)向下 

(x1-1,y1)->(x1,y1)向右 (x2,x1-1+y1-x2)->(x2,x1+y1-x2)向下 (x3-1,x1+y1-x3)->(x3,x1+y1-x3)向右  

(x1-1,y1)->(x1,y1)向右 (x2-1,x1+y1-x2)->(x2,x1+y1-x2)向右 (x3,x1-1+y1-x3)->(x3,x1+y1-x3)向下  

(x1-1,y1)->(x1,y1)向右 (x2-1,x1+y1-x2)->(x2,x1+y1-x2)向右 (x3-1,x1+y1-x3)->(x3,x1+y1-x3)向右 

(x1,y1-1)->(x1,y1)向下 (x2,x1+y1-1-x2)->(x2,x1+y1-x2)向下,(x3,x1+y1-1-x3)->(x3,x1+y1-x3)向下 

(x1,y1-1)->(x1,y1)向下 (x2,x1+y1-1-x2)->(x2,x1+y1-x2)向下,(x3-1,x1+y1-x3)->(x3,x1+y1-x3)向右 

(x1,y1-1)->(x1,y1)向下 (x2-1,x1+y1-x2)->(x2,x1+y1-x2)向右,(x3,x1+y1-1-x3)->(x3,x1+y1-x3)向下 

(x1,y1-1)->(x1,y1)向下 (x2-1,x1+y1-x2)->(x2,x1+y1-x2)向右,(x3-1,x1+y1-x3)->(x3,x1+y1-x3)向右 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=21;
int gi()
{
    int str=0;
    char ch=getchar();
    while(ch>'9' || ch<'0')ch=getchar();
    while(ch>='0' && ch<='9')str=str10+ch-48,ch=getchar();
    return str;
}
int f[N*2][N][N][N],a[N][N];
int main()
{
    int n=gi();
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            a[i][j]=gi();
    int tmp=0,from,to;
    for(int i=1; i<n+n; i++)
    {
        from=i-n+1>1?i-n+1:1;
        to=i<n?i:n;
        for(int j=from; j<=to; j++)
        {
            for(int k=from; k<=to; k++)
            {
                for(int g=from; g<=to; g++)
                {
                    tmp=0;
                    if(f[i-1][j][k][g]>tmp)tmp=f[i-1][j][k][g];
                    if(f[i-1][j-1][k][g]>tmp)tmp=f[i-1][j-1][k][g];
                    if(f[i-1][j][k-1][g]>tmp)tmp=f[i-1][j][k-1][g];
                    if(f[i-1][j][k][g-1]>tmp)tmp=f[i-1][j][k][g-1];
                    if(f[i-1][j-1][k-1][g]>tmp)tmp=f[i-1][j-1][k-1][g];
                    if(f[i-1][j-1][k][g-1]>tmp)tmp=f[i-1][j-1][k][g-1];
                    if(f[i-1][j][k-1][g-1]>tmp)tmp=f[i-1][j][k-1][g-1];
                    if(f[i-1][j-1][k-1][g-1]>tmp)tmp=f[i-1][j-1][k-1][g-1];
                    f[i][j][k][g]=tmp+a[j][i-j+1];
                    if(j!=k)f[i][j][k][g]+=a[k][i-k+1];
                    if(g!=k && g!=j)f[i][j][k][g]+=a[g][i-g+1];
                }
            }
        }
    }
    printf("%d",f[n+n-1][n][n][n]);
    return 0;
}

 

法二:
网络流——最大费用最大流。

具体算法如下:

拆点建图,每一个点都拆成两个点,在这里就称为出点和入点。

出点和入点建两条边,一条费用为s[i][j],流量为1;一条费用为0,流量为inf。(分别表示选择这个点和从这个点上经过)

将(i,j)的出点分别和(i+1,j)(i,j+1)的入点建边,流量为inf,费用为0。(表示行进)

跑一边最大费用最大流就可以了。

额~~~鼓掌鼓掌。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#define inf (2e8)
using namespace std;
int n,m;
int ans;
struct node
{
    int next,to,cost,cap;
} edge[500001];
int size=1,head[20001],s[51][51];
void putin(int from,int to,int cap,int cost)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].cap=cap;
    edge[size].cost=cost;
    head[from]=size;
}
void in(int from,int to,int cap,int cost)
{
    putin(from,to,cap,cost);
    putin(to,from,0,-cost);
}
void build()
{
    int i,j;
    in(0,1,3,0);//源点和1建边 
    in(n*n*2,n*n*2+1,3,0);//末节点和汇点建边 
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            in((i-1)*n+j,(i-1)*n+j+n*n,1,s[i][j]);//一条费用为s[i][j],流量为1的边 
            in((i-1)*n+j,(i-1)*n+j+n*n,inf,0);//一条费用为0,流量为inf的边 
            if(i!=n)in((i-1)*n+j+n*n,i*n+j,inf,0);
            if(j!=n)in((i-1)*n+j+n*n,(i-1)*n+j+1,inf,0);//和连接点建边,注意一下临界条件 
        }
    }
}
int f[300001],pre[300001],vis[300001];
bool bfs(int src,int des)
{
    int i;
    for(i=0; i<=n*n*2+1; i++)
    {
        f[i]=-1;
        vis[i]=0;
    }
    queue<int>mem;
    mem.push(src);
    f[src]=0;
    vis[src]=1;
    while(!mem.empty())
    {
        int x=mem.front();
        mem.pop();
        vis[x]=0;
        for(i=head[x]; i!=-1; i=edge[i].next)
        {
            int y=edge[i].to;
            if(edge[i].cap&&f[y]<f[x]+edge[i].cost)
            {
                f[y]=f[x]+edge[i].cost;
                pre[y]=i;
                if(!vis[y])
                {
                    mem.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    if(f[des]==-1)return 0;
    else return 1;
}//最大费用最大流,求的是最长路,因此初值为-1 
void change(int src,int des)
{
    int x=des,flow=2e8;
    while(x!=src)
    {
        flow=min(flow,edge[pre[x]].cap);
        x=edge[pre[x]^1].to;
    }
    x=des;
    while(x!=src)
    {
        ans+=flow*edge[pre[x]].cost;
        edge[pre[x]].cap-=flow;
        edge[pre[x]^1].cap+=flow;
        x=edge[pre[x]^1].to;
    }
}//反过来修改每一条边的流量,并计算ans的值,edge[i^1]为edge[i]的反向边 
void max_cost_flow(int src,int des)
{
    while(bfs(src,des))change(src,des);
}
int main()
{
    memset(head,-1,sizeof(head));
    int i,j;
    scanf("%d",&n);
    for(i=1; i<=n; i++)
        for(j=1; j<=n; j++)
            scanf("%d",&s[i][j]);
    build();
    max_cost_flow(0,n*n*2+1);
    printf("%d",ans);
    return 0;
}

 

 

 

 

posted @ 2017-06-06 15:35  kakakakakaka  阅读(571)  评论(3编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效