Matrix Again |
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) |
Total Submission(s): 16 Accepted Submission(s): 7 |
Problem Description Starvae very like play a number game in the n*n Matrix. A positive integer number is put in each area of the Matrix. |
Input The input contains multiple test cases. |
Output For each test case output the maximal values starvae can get. |
Sample Input 2 10 3 5 10 3 10 3 3 2 5 3 6 7 10 5 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 |
Sample Output 28 46 80 |
Author Starvae |
Source HDOJ Monthly Contest – 2010.04.04 |
Recommend lcy |
/* 题意:给你一个n*n的矩阵每个点都有相应的权值,然后让你求出从左上角走到右下角,再返回能讲过的最大权值是多少,但是两次的路 线不能经过同一个点。 #初步思路:最大费用最大流由于每个数只能取一次,所以对当前每一个数要进行拆点,将i拆为i和i‘‘,然后从i连接一条边到i‘‘ ,容量为1,费用为第i个点的费用,然后将i和取完i点后能取的数连一条边,容量为1,费用为0。因为是从(1,1)->(n,n)->(1, 1),所以我们建立超级源点S,连接S和第一个节点,容量为2(因为要走两条路径),费用为0,建立超级汇点T,连接点(n*n)‘‘ 到 T,容量为1,费用为0,然后求一次最大费用最大流,因为1和n*n这两个点算了两次,故需要减去一次他们的费用之和,发现网络流的题 目数组的大小很重要,之前因为数组问题出现了各种错误,望今后引起高度重视 #补充:超内存......用数组来模拟栈就过了 */ #include<bits/stdc++.h> using namespace std; int n; int mapn[610][610]; int tol=0; /**************************************************数组模拟************************************************************/ const int MAXN = 720010; const int MAXM = 3501000; const int INF = 1<<30; struct EDG{ int to,next,cap,flow; int cost; //单价 }edg[MAXM]; int head[MAXN],eid; int pre[MAXN], cost[MAXN] ; //点0~(n-1) void init(){ eid=0; memset(head,-1,sizeof(head)); } void addEdg(int u,int v,int cap,int cst){ edg[eid].to=v; edg[eid].next=head[u]; edg[eid].cost = cst; edg[eid].cap=cap; edg[eid].flow=0; head[u]=eid++; edg[eid].to=u; edg[eid].next=head[v]; edg[eid].cost = -cst; edg[eid].cap=0; edg[eid].flow=0; head[v]=eid++; } bool inq[MAXN]; int q[MAXN]; bool spfa(int sNode,int eNode , int n){ int l=0,r=0; for(int i=0; i<n; i++){ inq[i]=false; cost[i]= -1; } cost[sNode]=0; inq[sNode]=1; pre[sNode]=-1; q[r++]=sNode; while(l!=r){ int u=q[l++]; //数组模拟 if(l==MAXN)l=0; inq[u]=0; for(int i=head[u]; i!=-1; i=edg[i].next){ int v=edg[i].to; if(edg[i].cap-edg[i].flow>0 && cost[v]<cost[u]+edg[i].cost){ //在满足可增流的情况下,最小花费 cost[v] = cost[u]+edg[i].cost; pre[v]=i; //记录路径上的边 if(!inq[v]){ if(r==MAXN)r=0; q[r++]=v; inq[v]=1; } } } } return cost[eNode]!=-1; //判断有没有增广路 } //反回的是最大流,最小花费为minCost int minCost_maxFlow(int sNode,int eNode ,int& minCost , int n){ int ans=0; while(spfa(sNode,eNode , n)){ for(int i=pre[eNode]; i!=-1; i=pre[edg[i^1].to]){ edg[i].flow+=1; edg[i^1].flow-=1; minCost+=edg[i].cost; } ans++; if(ans==2)break; } return ans; } /**************************************************数组模拟************************************************************/ int main(){ // freopen("in.txt","r",stdin); while(scanf("%d",&n)!=EOF){ //cout<<n<<endl; tol=n*n;//总的点数 init(); // 首先是初始化,总共可能n*n个点 0到n*n-1 // 但是这个题还需要回来,所以可能走的点就是 // 2*n*n for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ scanf("%d",&mapn[i][j]); } } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i||j){ //如果这个点不是起点 addEdg(i*n+j, i*n+j+n*n , 1 , mapn[i][j]); //如果这个点还没有到达右边的边界 if(j+1<n)//向右边走一步 addEdg(i*n+j+n*n, i*n+j+1 , 1 , 0 ); //如果这个点还没有到达下面的边界 if(i+1<n)//想下边走一步 addEdg(i*n+j+n*n, (i+1)*n+j , 1 , 0); } else{//如果这个点是原点的话 addEdg(0 , 1 , 1,0) , addEdg(0 , n , 1 , 0); } } } int ans=0; minCost_maxFlow(0,tol-1,ans,tol*2); ans+=mapn[0][0]; if(n>1) ans+=(mapn[n-1][n-1]); printf("%d\n",ans); } return 0; }