hdu 2686 && 3376 Matrix

最近看校招题发现搜狗2013的一道,程序员编程艺术第三十四格子取数问题出自于此,再来温习一遍。

dp方法

走个来回相当于从左上角到右下角走2次。

由于只能走右和下,所以可以以步数来划分状态。

0  1  2  3

1  2  3  4

2  3  4  5

3  4  5  6

第二组数据

3

10 3 3

2 5 3

6 7 10

可以转换为

10

2 3

6 5 3

* 7 3

* * 10

行为步数,[i][j]位置只能从[i-1][j],[i-1][j-1]过来。

dp[i][j][k] 表示第i步时取的2个数分别在j和k列时的最大值

转移方程为dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-1][k],dp[i-1][j][k-1],dp[i-1][j-1][k-1])

+map[i][j]+map[i][k];

#include<iostream>
using namespace std;
int map[1205][605];
int f[2][605][605];
int n,m;
inline int MAX(int a,int b)
{
    return (a>b)? a:b;
}
int main()
{
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        memset(map,-1,sizeof(map));
        memset(f,-1,sizeof(f));
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                    scanf("%d",&map[i+j-1][j]);
        f[0][1][2]=map[1][1]+map[2][1]+map[2][2];
        int flag=1;
        m=2*n-2;
        for(i=3;i<=m;i++)
        {
            j=i-n+1;
            if(j<1) j=1;
            for(;j<=i && j<=n;j++)
                for(k=j+1;k<=i && k<=n;k++)
                {
                    if(j==1)
                        f[flag][j][k]=MAX(f[1-flag][j][k],f[1-flag][j][k-1])                                   
                        +map[i][j]+map[i][k];
                    else
                        f[flag][j][k]=MAX(MAX(f[1-flag][j][k],f[1-flag][j][k-1]),
                                   MAX(f[1-flag][j-1][k],f[1-flag][j-1][k-1]))
                                   +map[i][j]+map[i][k];
                }
            flag=!flag;
        }
        int ans=f[0][n-1][n]+map[2*n-1][n];
        printf("%d\n",ans);                        
    }
    return 0;
}

费用流方法

将每个点拆为2个,新点和旧点连接流量1费用-map[i][j]的边

原来的点分别和它右下2个方向的新点连接流量1费用0的边

源点和左上角的点连接流量2费用0的边

右下角的新点和终点连接流量2费用0的边

求最小费用即可

#include <iostream>
using namespace std;
const int inf=0x3f3f3f3f;
struct rec
{
    int u,v,w,c,link;
}edge[3000000];
int head[800000],pre[800000],dis[800000],Q[20000000];
bool vis[800000];
int map[605][605];
int n,num,st,ed,ans,maxflow,l,r;
inline void addedge(int u,int v,int w,int c)
{
    edge[num].u=u;
    edge[num].v=v;
    edge[num].w=w;
    edge[num].c=c;
    edge[num].link=head[u];
    head[u]=num++;
    edge[num].u=v;
    edge[num].v=u;
    edge[num].w=0;
    edge[num].c=-c;
    edge[num].link=head[v];
    head[v]=num++;
}
int fee_maxflow()
{
    int ans=0,k=0,u,a,v,i;
    while(1)
    {
        for(i=0;i<=ed;i++)
        {
            dis[i]=inf;
            vis[i]=false;
        }
        dis[st]=0;
        l=0;
        r=1;
        Q[0]=st;
        pre[st]=0;
        while(l!=r)
        {
            a=Q[l++];
            vis[a]=false;
            for(i=head[a];i;i=edge[i].link)
            {
                v=edge[i].v;
                if(edge[i].w && dis[v]>dis[a]+edge[i].c)
                {
                    dis[v]=dis[a]+edge[i].c;
                    pre[v]=i;
                    if(!vis[v])
                    {
                        Q[r++]=v;
                        vis[v]=true;
                    }
                } 
            } 
        } 
        if(dis[ed]==inf)
            break;
        /*k=inf;    
        for(u=ed;pre[u];u=edge[pre[u]].u)
        {
            if(k>edge[pre[u]].w)
                k=edge[pre[u]].w;
        }*/
        for(u=ed;pre[u];u=edge[pre[u]].u)
        {
            edge[pre[u]].w--;
            edge[pre[u]^1].w++;
        }
        ans+=dis[ed];
    }
    return ans;
}
int main()
{
    int i,j;
    while(~scanf("%d",&n))
    {
        st=0;
        ed=2*n*n-1;
        memset(head,0,sizeof(head));
        num=2;
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&map[i][j]);
        addedge(0, n * n, 2, 0);
        for(i=0;i<n;i++)
            for(j=0;j<n;j++) 
            {
                if (i + 1 < n)
                    addedge(i * n + j + n * n, i * n + n + j, 1, 0);
                if (j + 1 < n)
                    addedge(i * n + j + n * n, i * n + j + 1, 1, 0);
                if (i == 0 && j == 0 || i == n - 1 && j == n - 1)
                    continue;             
                addedge(i * n + j, i * n + j + n * n, 1, -map[i][j]);
           }
        addedge(n * n - 1, n * n * 2 - 1, 2, 0);

        /*for(i=2;i<num;i++)
            cout<<i<<' '<<edge[i].u<<"-->"<<edge[i].v<<' '<<edge[i].c<<endl;*/
        printf("%d\n",-fee_maxflow()+map[0][0]+map[n-1][n-1]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值