• hdu 2686 Matrix 最小费用最大流 或 多线程DP


    /*
    hdu 2686 Matrix
    方阵(每个格子里面都有一个数字)里面从左上走到右下,再回来,一个点只能走一次,求数字之和最大是多少
    相当与从左上到右下选两条不交叉的路,使和最大
    典型的最大费用最大榴
    
    我要说的是,这是昨天的省赛的原题,当时就像到了什最大费用最大流,但是当时脑袋一热,又感觉不是(韩式我对这个算法的理解不够啊)
    其实只要在那个模板(http://blog.csdn.net/qq172108805/article/details/7857503)的基础上该一行就可以了(T﹏T)
    */
    #include<stdio.h>
    #include<string.h>
    #include<queue>
    using namespace std;
    struct node
    {
    	int u,v,f,c,next;
    }e[100000];
    int n,k,head[5010],yong,s,t,maxflow;
    int map[55][55];
    int pre[5010],dist[5010],vis[5010];
    void adde(int u,int v,int c,int f)//加边
    {
    	e[yong].u=u,e[yong].v=v,e[yong].c=c,e[yong].f=f;
    	e[yong].next=head[u],head[u]=yong++;
    
    	e[yong].u=v,e[yong].v=u,e[yong].c=-c,e[yong].f=0;//这是它的退边
    	e[yong].next=head[v],head[v]=yong++;
    }
    int spfa()//spfa求最短路
    {
    	int i,u,v;
    	for(i=0;i<=t;i++)
    		pre[i]=-1,vis[i]=0,dist[i]=0x7fffffff;
    	dist[s]=0;
    	vis[s]=1;
    	queue<int>q;
    	q.push(s);
    	while(!q.empty())
    	{
    		u=q.front();
    		q.pop();
    		for(i=head[u];i!=-1;i=e[i].next)
    		{
    			v=e[i].v;
    			if(e[i].f>0&&dist[u]+e[i].c<dist[v])
    			{
    				dist[v]=dist[u]+e[i].c;
    				pre[v]=i;//标记的是走到这儿的那条边
    				if(!vis[v])
    				{
    					vis[v]=1;
    					q.push(v);
    				}
    			}
    		}
    		vis[u]=0;
    	}
    	if(dist[t]==0x7fffffff)
    		return 0;
    	return 1;
    }
    int min(int a,int b){return a<b?a:b;}
    void add()//加流  修改残留网络
    {
    	int v;
    	int mm=0x7fffffff;
    	for(v=pre[t];e[v].u!=s;v=pre[e[v].u])//求最小的 可增流
    		mm=min(mm,e[v].f);
    	for(v=pre[t];e[v].u!=s;v=pre[e[v].u])
    	{
    		e[v].f-=mm;//修改残留网络
    		e[v^1].f+=mm;
    		maxflow+=mm*e[v].c;//加到费用里边
    	}
    }
    int main()
    {
    	int i,j,b;
    	while(scanf("%d",&n)!=EOF)
    	{
    	    k=2;//其实我就只是该了这里!!!其余的跟我的模板一模一样 http://blog.csdn.net/qq172108805/article/details/7857503
    		maxflow=0;//初始化
    		s=n*n*2;
    		t=s+1;
    		yong=0;
    		memset(head,-1,sizeof(head));
    		for(i=1;i<=n;i++)//读数据
    			for(j=1;j<=n;++j)
    				scanf("%d",&map[i][j]);
    
    		for(i=1;i<=n;i++)//拆点建边
    			for(j=1;j<=n;++j)
    			{
    				b=(i-1)*n+j-1;//点的编号是0~n*n-1
    				adde(b*2,b*2+1,-map[i][j],1);//
    				adde(b*2,b*2+1,0,k-1);
    			}
    		for(i=1;i<=n;i++)//向右建边
    			for(j=1;j<n;j++)
    			{
    				b=(i-1)*n+j-1;
    				adde(b*2+1,2*(b+1),0,k);
    			}
    		for(i=1;i<n;++i)//向下建边
    			for(j=1;j<=n;++j)
    			{
    				b=(i-1)*n+j-1;
    				adde(b*2+1,2*(b+n),0,k);
    			}
    		adde(s,0,0,k);//头
    		adde(n*n*2-1,t,0,k);//尾
    
    		while(spfa())
    			add();
    
    		printf("%d\n",-maxflow);//再取相反数
    	}
    	return 0;
    }

    下面的是用双线程DP做的

    /*
    还是上面那个题,用双线程dp做的
    
    让两个进程同时进行,枚举步数K,当x1==x2||y1==y2时跳过,得状态转移方程:
    dp(k,x1,y1,x2,y2)=max(dp(k-1,x1-1,y1,x2-1,y2), dp(k-1,x1-1,y1,x2,y2-1),dp(k-1,x1,y1-1,x2-1,y2),dp(k-1,x1,y1-1,x2,y2-1))+data(x1,y1)+data(x2,y2);
    dp(k-1,x1-1,y1,x2-1,y2)第一条路来自上面,第二条路来自上面
    dp(k-1,x1-1,y1,x2,y2-1)第一条路来自上面,第二条路来自左面
    dp(k-1,x1,y1-1,x2-1,y2)第一条路来自左面,第二条路来自上面
    dp(k-1,x1,y1-1,x2,y2-1)第一条路来自左面,第二条路来自左面
    由于只能走右或下,所以坐标满足x+y=k。这样就能降低维数为3维(y1=k-x1,y2=k-x2),方程:
    dp(k,x1,x2)=max(dp(k-1,x1,x2),dp(k-1,x1-1,x2),dp(k-1,x1,x2-1),dp(k-1,x1-1,x2-1))+data(x1,k-x1)+data(x2,k-x2);
    
    但是我的数组开成
    int maps[55][55];
    int dp[100][55][55];
    (55*55+100*55*55)*4=1193k<32768K为什么老是返回WA
    
    
    某个博客上说的好,DP问题,增加限制就意味着增加维度
    */
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int maps[40][40];
    int dp[80][40][40];
    int n;
    int Max(int a,int b,int c,int d)
    {
        return max(a,max(b,max(c,d)));
    }
    int main()
    {
        int i,j,k;
        while(scanf("%d",&n)!=EOF)
        {
            for(i=0; i<n; ++i)
            {
                for(j=0; j<n; ++j)
                {
                    scanf("%d",&maps[i][j]);
                }
            }
            memset(dp,0,sizeof(dp));
            for(k=1; k<2*n-2; ++k)
            {
                for(i=0; i<n; ++i)
                {
                    for(j=0; j<n; ++j)
                    {
                        if(i==j) continue;
                        dp[k][i][j]=Max(dp[k-1][i][j],dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i-1][j-1]);
                        dp[k][i][j]+=maps[i][k-i]+maps[j][k-j];
                    }
                }
            }
            int ans=max(dp[k-1][n-1][n-2],dp[k-1][n-2][n-1])+maps[0][0]+maps[n-1][n-1];
            printf("%d\n",ans);
        }
        return 0;
    }
    


  • 相关阅读:
    INT最值
    约瑟夫问题
    word里的图片怎么复制出来
    必须掌握的8个dos命令
    vld(Visual Leak Detector 内存泄露检测工具
    sscanf,sscanf_s及其相关用法
    游戏开发梦开始的地方笔记
    关于字符编码,你所需要知道的
    CreateMutex创建互斥体可以用于只运行进程的一个实例
    sprintf,你知道多少?
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3076863.html
Copyright © 2020-2023  润新知