/*
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;
}