hdu 2686 费用流 / 双线程DP

题意:给一个方阵,求从左上角出到右下角(并返回到起点),经过每个点一次不重复,求最大获益(走到某处获得改点数值),下来时每次只能向右或向下,反之向上或向左。

俩种解法:

1  费用流法:思路转化:从左上角流出2的流量,(表示走俩条路),归于右下角,可以走就有边(右和下),权为负的费用,源点连起点,汇点连终点,流量为2. 除源汇外所有点一分为2,Y向X对应点有流量1的边,之前边为X到Y的(原图),这样处理解决每个点只最多走一次(除了源汇外)(X部只出,Y部要出必先回到X对应点)。跑最小费用最大流即可。

2:dp法:(感谢XX大牛的提示)俩个点同时走,走了第K步状态:为x1,y1;x2,y2,  由于(x1+y1=k,x2+y2=k),状态压缩为3维,每个状态表示当前这步俩个点的横左边。

dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1])

方法1:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
int a[50][50];
int nume=0;int e[50000][4];int head[2000];
int n;
void inline  adde(int i,int j,int c,int w)
{
    e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
    e[nume][2]=c;e[nume++][3]=w;
    e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
    e[nume][2]=0;e[nume++][3]=-w;
}
int inq[2000];int pre[2000];int prv[2000];
int d[2000];
bool spfa(int &sum)
{
    for(int i=0;i<=2*n*n+2;i++)
          {
              inq[i]=0;
              d[i]=inf;
          }
    queue<int>q;
    q.push(2*n*n);
    inq[2*n*n]=1;
    d[2*n*n]=0;
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        inq[cur]=0;
        for(int i=head[cur];i!=-1;i=e[i][1])
        {
            int v=e[i][0];
            if(e[i][2]>0&&d[cur]+e[i][3]<d[v])
            {
                d[v]=d[cur]+e[i][3];
                pre[v]=i;
                prv[v]=cur;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
    if(d[2*n*n+1]==inf)return 0;
    int cur=2*n*n+1;int minf=inf;
    while(cur!=2*n*n)
    {
        minf=e[pre[cur]][2]<minf?e[pre[cur]][2]:minf;
        cur=prv[cur];
    }
     cur=2*n*n+1;
    while(cur!=2*n*n)
    {
        e[pre[cur]][2]-=minf;
        e[pre[cur]^1][2]+=minf;
        cur=prv[cur];
    }
    sum+=minf*d[2*n*n+1];
    return 1;
}

int mincost()
{
    int sum=0;
    while(spfa(sum));
    return sum;
}
void init()
{
    nume=0;
    memset(head,-1,sizeof(head));
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
             scanf("%d",&a[i][j]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                if(i+1<n)
                adde(i*n+j,(i+1)*n+j+n*n,1,-a[i+1][j]);
                if(j+1<n)
                adde(i*n+j,i*n+j+1+n*n,1,-a[i][j+1]);
            }
        for(int i=0;i<n*n;i++)
        {
            adde(i+n*n,i,1,0);
        }
         adde(2*n*n,0,2,0);
        adde(2*n*n-1,2*n*n+1,2,0);
        int ans=-mincost();
       ans+=a[0][0];
       ans-=a[n-1][n-1];
        printf("%d\n",ans);
    }
    return 0;
}

方法2

;

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int a[50][50];
int dp[80][50][50];
int max(int x,int y,int z,int t)
{
    if(x>=y&&x>=z&&x>=t)return x;
    if(y>=x&&y>=z&&y>=t)return y;
    if(z>=x&&z>=y&&z>=t)return z;
    return t;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
             scanf("%d",&a[i][j]);
           dp[0][0][0]=a[0][0];
           dp[1][0][1]=a[0][1]+a[0][0]+a[1][0];

          for(int k=1;k<=2*n-2;k++)
          {
              for(int x1=0;x1<=k&&x1<n;x1++)
              {
                  for(int x2=0;x2<=k&&x2<n;x2++)
                  {
                      if(x1!=x2)
                      {
                          if(x1>0&&x2>0)
                        dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1]);
                        else if(x1==0&&x2>0)
                        {
                            dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],0,0);
                        }
                        else
                        {
                            dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1-1][x2],0,0);
                        }
                        dp[k][x1][x2]+=(a[x1][k-x1]+a[x2][k-x2]);

                      }
                  }
              }
          }
          int ans=dp[2*n-3][n-2][n-1]+a[n-1][n-1];
          cout<<ans<<endl;
    }
    return 0;
}

hdu 2686 费用流 / 双线程DP

时间: 2024-10-01 08:06:22

hdu 2686 费用流 / 双线程DP的相关文章

蓝桥杯 - 传纸条 (双线程DP)

题目传送:传纸条 思路:双线程DP,具体看代码; AC代码: #include <map> #include <set> #include <cmath> #include <deque> #include <queue> #include <stack> #include <cstdio> #include <cctype> #include <string> #include <vecto

浅谈双线程dp (nyoj61 nyoj712)经典【传字条】和【探 寻 宝 藏】

浅谈双线程dp 先看问题: 传纸条(一) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是,他们可以通过传纸条来进行交流.纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n).从小渊传到小轩的纸条只可以向下或者向右传递,从小轩

[Swust OJ 1084]--Mzx0821月赛系列之情书(双线程dp)

题目链接:http://acm.swust.edu.cn/problem/1084/ Time limit(ms): 1000 Memory limit(kb): 65535 Description 小时候,Mzx0821暗恋班上的一个妹子Zzx. 一次班上做活动,班上同学被安排坐成m行n列的矩阵,Mzx0821坐在坐标(x1,y1)的位置,Zzx坐在坐标(x2,y2)的位置.活动过程中,Mzx0821写了一张纸条想给Zzx,但是Mzx0821又不想班上其他人看到他写的内容,于是Mzx0821给

TYVJ 1011 传纸条(双线程DP)

P1011 传纸条 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 NOIP2008复赛提高组第三题 描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是,他们可以通过传纸条来进行交流.纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n).从小渊传到小轩的纸条

HDU 3376 费用流 Matrix Again

咳咳,内个,内什么,白书上的费用流模板没过,后来换了种存图方式才过. 题意: 给出一个n × n的矩阵,每个格子中有一个数字代表权值,找出从左上角出发到右下角的两条不相交的路径(起点和终点除外),使得两条路径权值之和最大. 分析: 如果n比较小的话是可以DP的,但是现在n非常大,DP会超时的. 这个用费用流来求解: 因为每个点只能走一次,所以先拆点,两点之间连一条容量为1费用为对应格子权值的边,如果是起点或终点,因为要走两次,所以要连容量为2的边. 对于不同格子之间,如果能到达,连一条容量为IN

hdu 5045 费用流

滚动建图,最大费用流(每次只有就10个点的二分图).复杂度,m/n*(n^2)(n<=10),今年网络赛唯一网络流题,被队友状压DP秒了....难道网络流要逐渐退出历史舞台???.... #include<iostream> //78ms #include<cstdio> #include<queue> using namespace std; const double inf =0x3f3f3f3f; const int maxv=50,maxe=500; in

HDU 5045 费用流求最大权

点击打开链接 题意:有n个人和m到题目,每个人做对的概率以矩阵形式给出,问如何分配才可以使做对的概率最大,有一个限制条件是做到目前为止每两个人的做题数量差距不能超过1,也就是前n道题目,必须一人做一个 思路:网上都是dp多一点,用网络流也可以,不过麻烦很多,可是本弱是一点dp都不会的选手啊,只能用网络流了,对于那个限制条件,我们可以以前n道题建一次图,然后再来n个,不过就直接建完就可以了,然后我们要求的是什么呢,很明显是最大权,而最大费用最大流刚好可以解决,这里面的费用流有两种方法,用spfa找

Going Home HDU - 1533 (费用流)

Going Home HDU - 1533 1 //费用流初探 2 #include <iostream> 3 #include <queue> 4 #include <cstring> 5 #include <cstdio> 6 #include <algorithm> 7 using namespace std; 8 const int inf = 0x3f3f3f3f; 9 const int maxn = 110; 10 char gra

hdu 4406 费用流

这题问题就是当前时刻到底选择哪门课程,易知选择是和分数有关的,并且是一个变化的权值,所以可以用拆点的方式,把从基础分到100分都拆成点,但若这样拆点的话,跑费用流时就必须保证顺序,这样就麻烦了..观察公式,发现同一门课,分数越高,权值是越低的,所以这是一个单调的,这样的话就可以对每一个分数建一条边,费用流会一条一条的跑. 注意将课程放在X集 #include<cstdio> #include<queue> #include<algorithm> #include<