hdu2686Matrix && hdu3376 Matrix Again

 这两道题目除了数据大小不同外是一模一样的

题目大意是给定一个n*n的矩阵,yifenfei从起点(1, 1)这个位置一直取数到(n,n),每取完一个数,下一个只能取当前数右方或者下方的一个数,(注意两个数之间的距离应该是1,之前以为下方或者右方任何一个数都可以取),就这样取到(n,n),然后再从(n,n)取回(1,1),这次每取完一个数,下一个只能取当前数左方或者右方的一个数,最后回到(1,1),每个数只能被取一次,求这样进行取数之后能取到的最大数

这题可用最大费用最大流来求解,由于每个数只能取一次,所以对当前每一个数要进行拆点,将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这两个点算了两次,故需要减去一次他们的费用之和,发现网络流的题目数组的大小很重要,之前因为数组问题出现了各种错误,望今后引起高度重视

 注:此为3376代码

#include <iostream>
using namespace std;
const int MAXN = 610*610*2+2;
const int MAXM = 4*MAXN;
const int inf = 1<<28;
struct node
{
       int from, to, next, value, cost;       
}mapp[MAXM];
int id;//记录当前节点标号 
int ffa[MAXN];//记录当前节点边 
void init()
{
     id = 0;
     memset(ffa, -1, sizeof(ffa));     
}
void addedge(int u, int v, int w, int c)
{
     mapp[id].from = u, mapp[id].to = v, mapp[id].value = w, mapp[id].cost = c, mapp[id].next = ffa[u], ffa[u] = id ++;        
     
     mapp[id].from = v, mapp[id].to = u, mapp[id].value = 0, mapp[id].cost = -c, mapp[id].next = ffa[v], ffa[v] = id ++;        
}
int pre[MAXN];//记录当前最小花费路径 
int pos[MAXN];//记录当前最短路径中 的节点标号
int inque[MAXN];
int dist[MAXN];//记录单源最短路径 
int que[10*MAXM];
bool SPFA(int s, int e, int n)
{
     memset(pre, -1, sizeof(pre));
     memset(inque, false, sizeof(inque));
     for (int i = 0; i <= n; i ++){
         dist[i] = -inf;    
     }
     dist[s] = 0;
     int rear, front;
     rear = front = 0;
     que[rear ++] = s;
     inque[s] = true;
     pre[s] = s;// 
     while (front < rear){
           
           int pr = que[front ++];
           inque[pr] = false;
           for (int i = ffa[pr]; i != -1; i = mapp[i].next){
               int fro = mapp[i].from, to = mapp[i].to;
               if (mapp[i].value > 0 && dist[to] < dist[fro] + mapp[i].cost){
                  dist[to] = dist[fro] + mapp[i].cost;
                  pre[to] = fro, pos[to] = i;
                  if (!inque[to]){//
                     que[rear ++] = to;
                     inque[to] = true;
                  }
               }    
               
           }      
     }
     return (pre[e] != -1 && dist[e] > -inf);
}
int flow, cost;
int minCostflow(int s, int e, int n)
{
     
     flow = cost = 0;
     while (SPFA(s, e, n)){
           int min_flow = INT_MAX;
           for (int i = e; i != s; i = pre[i]){//找到当前路径中的瓶颈流量 
               if (min_flow > mapp[pos[i]].value){
                  min_flow = mapp[pos[i]].value; 
               }
           }    
           if (!min_flow)continue;
           flow += min_flow;
           cost += dist[e];
           
           for (int i = e; i != s; i = pre[i]){
               mapp[pos[i]].value -= min_flow;
               mapp[pos[i]^1].value += min_flow;
           }
     }
     return cost;
} 
void pri(int n)
{
     for (int i = 1; i <= n; i ++){
         cout<<"i:"<<i<<endl;
         for (int j = ffa[i]; j != -1; j = mapp[j].next){
             if (mapp[j].value)
             cout<<mapp[j].to<<' '<<mapp[j].value<<' '<<mapp[j].cost<<endl;    
         }    
     }     
}
const int Go[2][2] = {{1, 0}, {0, 1}};
int n;
bool check(int x,int y)
{
     return (x > 0 && x <= n && y > 0 && y <= n);     
}
int mat[610][610];
int num[MAXN];
int main()
{
    //freopen("IN.txt", "r", stdin);
    //freopen("OUT.txt", "w", stdout);
    while (scanf("%d", &n) != EOF){
          init();
          
          int kao = 1;
          for (int i = 1; i <= n; i ++){
              for (int j = 1; j <= n; j ++){
                  scanf("%d", &mat[i][j]);
                  num[kao ++] = mat[i][j];    
              }    
          }
         /*for (int i = 1; i <= n*n; i ++){
              cout<<num[i]<<' ';
          }
          cout<<endl;*/
          for (int i = 1; i <= n*n; i ++){
              if (i == 1)addedge(i, i + n*n, 2, num[i]);
              else if (i == n*n) addedge(i, i + n*n, 2, num[i]);
                   else  addedge(i, i + n*n, 1, num[i]);
          }
          for (int i = 1; i <= n; i ++){
              for (int j = 1; j <= n; j ++){
                  //cout<<"j:"<<j<<endl;
                  for (int k = 0; k < 2; k ++){
                      //for (int l = 1; l <= n; l ++){
                          int xx = i + Go[k][0];
                          int yy = j + Go[k][1];
                          if (!check(xx, yy))continue;
                          //cout<<"("<<i<<","<<j<<")->("<<xx<<","<<yy<<")"<<endl; 
                          addedge((i-1)*n + j + n*n, (xx-1)*n + yy, 1, 0);    
                      //}
                  }
              }    
          }
          //pri(2*n*n+2);
          int s = 2*n*n + 1, t = 2*n*n + 2;
          addedge(s, 1, 2, 0);
          addedge(2*n*n, t, 2, 0);
          //
          printf("%d\n", minCostflow(s, t, 2*n*n+2)-(mat[1][1]+mat[n][n]));
    }
    return 0;    
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值