[NOIP2008]传纸条

先用双线动规写一遍,下午再来写费用流解法= =

解法1:双线动规

     首先我们不难看出,由于纸条回来时不能与去时的路径重叠,“一来一回”和“从左上角分两条路走向右下角”这两个模型是等价的。于是我们可以把两条路到达的端点同时作为状态保存下来(dp[x1][y1][x2][y2])。又因为矩阵图的特殊性,左上角到右下角的所有路径长度均为两点的曼哈顿距离,我们可以让两点”同时“移动,即任何时刻两点走过的路程相同。这样,我们可以记当前状态为dp[i, j, k],其中 i 表示当前两点走到的横纵坐标之和,j表示第一条路径走到的横坐标,k表示第二条路径走到的横坐标。考虑到两条路径在途中不能重叠,我们约定j > k。其中每个位置最多都可以由两个点达到,那么每种状态最多要考虑2*2=4种前驱。这里要考虑一种特殊情况:当k == j-1时,两点都可以由(k, i-k-1)这一点走到,然而题目中规定路径中不能有重叠,那么这时我们应当排除”两点从同一点转移得到“的情况╮(╯▽╰)╭

    这样,这道题就完美解决了→_→时间复杂度为O((M+N)MN)

    p.s.截至发表前,这个解法在河南省实验中学COGS上排到了速度rank1→_→ (5ms)

ExpandedBlockStart.gif
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <cctype>
 5 #include <cmath>
 6  #define maxn (52)
 7  using  namespace std;
 8 #ifdef DEBUG
 9 FILE * in = fopen( " test ", " r ");
10  #define out stdout
11  #else
12 FILE * in = fopen( " message.in ", " r ");
13 FILE * out = fopen( " message.out ", " w ");
14  #endif
15 
16 inline  void getint( int &k){
17      char c = fgetc( in);
18      while(!isdigit(c))c = fgetc( in);
19     k = c -  ' 0 ';
20      while(isdigit(c = fgetc( in)))
21         k = k *  10 + (c -  ' 0 ');
22 }
23  int m, n;
24  int Mat[maxn][maxn]; // 坐标从1开始
25  int dp[maxn<< 1][maxn][maxn] = { 0};
26  int main(){
27      int i, j, k, Max, t;
28     getint(m),getint(n);
29      for(i =  1;i <= m;++i)
30          for(j =  1;j <= n;++j)getint(Mat[i][j]);
31     dp[ 3][ 2][ 1] = Mat[ 2][ 1] + Mat[ 1][ 2];
32      for(i =  4;i < m+n;++i)
33          for(j =  2;j <= m;++j){
34              if(j == i) break;
35              for(k = max( 1,i-n);k < j;++k){
36                 Max = dp[i- 1][j][k];
37                  if((t=dp[i- 1][j][k- 1]) > Max)Max = t;
38                  if((t=dp[i- 1][j- 1][k- 1]) > Max)Max = t;
39                  if(k!=j- 1 && (t=dp[i- 1][j- 1][k])>Max)Max = t;
40                 dp[i][j][k] = Max + Mat[j][i-j] + Mat[k][i-k];
41             }
42         }
43     i = m + n -  1;
44     fprintf( out, " %d\n ",dp[i][m][m- 1]);
45      return  0;
46 }
双线动规

 解法2:最大费用最大流

    好吧现在已经是第二天了……感觉费用流写起来很费时间啊QAQ……

    这道题的建模思路是这样的……将每个学生抽象成一条有向边,纸条抽象为流量,好心值抽象为费用,考虑以下两点限制条件:1.最多为2的流量流经(1,1)和(m,n)两学生;2.中间每个学生对应的容量为1(每个学生只会传一次纸条);3.在相邻的两个学生中,从偏左、偏上的学生对应边的终点向另一学生对应边的起点连边,容量为1,费用为0;4.源点为(1,1)学生的起点,汇点为(m,n)学生的终点。

    对这样一个有向网络建图,求出最大费用最大流,即为原问题的解。由于问题的特殊性,spfa运行次数接近常数,因此这一算法的时间复杂度接近最短路。(然而,对于这样小的网格,费用流的解法其实有些浪费……)STL容器的时间常数似乎有点大,这个解法在COGS上花了9毫秒(而且是打开了-O2优化……醉了醉了)

 

ExpandedBlockStart.gif
  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <cctype>
  5 #include <cmath>
  6 #include <vector>
  7 #include <queue>
  8 #include <deque>
  9 #include <ctime>
 10  #define Vect vector<edge*>
 11  #define pb push_back
 12  #define iter(v) v::iterator
 13  #define bg begin()
 14  #define maxn (52)
 15  #define maxv (5002)
 16  using  namespace std;
 17 
 18  #if defined DEBUG
 19 FILE * in = fopen( " test ", " r ");
 20  #define out stdout
 21  #else
 22 FILE * in = fopen( " message.in ", " r ");
 23 FILE * out = fopen( " message.out ", " w ");
 24  #endif
 25 
 26 inline  void getint( int &k){
 27      char c = fgetc( in);
 28      while(!isdigit(c))c = fgetc( in);
 29     k = c -  ' 0 ';
 30      while(isdigit(c = fgetc( in)))
 31         k = k *  10 + (c -  ' 0 ');
 32 }
 33 
 34  struct edge{
 35      int w, vol, to;
 36     edge(){}
 37     edge( int W,  int V,  int T):w(W),vol(V),to(T){}
 38 }E[maxv* 6];
 39  int preE[maxv];
 40  int Ecnt =  0;
 41 Vect adj[maxv];
 42  int m, n, Vnum =  2, preV[maxv], ans =  0;
 43 inline  void addE( int &Ecnt,  int f,  int t,  int w,  int v){
 44     E[Ecnt] = edge(w,v,t);
 45     adj[f].pb(E + Ecnt++);
 46     E[Ecnt] = edge(-w, 0,f);
 47     adj[t].pb(E + Ecnt++);
 48 }
 49 
 50 inline  void init(){
 51      int i, j, w;
 52     getint(w);
 53     addE(Ecnt,  01, w,  2);
 54      for(i =  1;i < n; ++i){
 55         getint(w);
 56         addE(Ecnt, Vnum- 1, Vnum,  01);
 57         addE(Ecnt, Vnum, Vnum+ 1, w,  1);
 58         Vnum +=  2;
 59     }
 60      for(i =  1;i < m- 1; ++i){
 61         getint(w);
 62         addE(Ecnt, Vnum-(n<< 1)+ 1, Vnum,  01);
 63         addE(Ecnt, Vnum, Vnum+ 1, w,  1);
 64         Vnum +=  2;
 65          for(j =  1;j < n; ++j){
 66             getint(w);
 67             addE(Ecnt, Vnum- 1, Vnum,  01);
 68             addE(Ecnt, Vnum-(n<< 1)+ 1, Vnum,  01);
 69             addE(Ecnt, Vnum, Vnum+ 1, w,  1);
 70             Vnum +=  2;
 71         }
 72     }
 73     getint(w);
 74     addE(Ecnt, Vnum-(n<< 1)+ 1, Vnum,  01);
 75     addE(Ecnt, Vnum, Vnum+ 1, w,  1);
 76     Vnum +=  2;
 77      for(j =  1;j < n; ++j){
 78         getint(w);
 79         addE(Ecnt, Vnum- 1, Vnum,  01);
 80         addE(Ecnt, Vnum-(n<< 1)+ 1, Vnum,  01);
 81          if(j < n- 1)addE(Ecnt, Vnum, Vnum+ 1, w,  1);
 82          else addE(Ecnt, Vnum, Vnum+ 102);
 83         Vnum +=  2;
 84     }
 85 }
 86  bool spfa( int &d){
 87      int dis[maxv] = { 0}, tmp, t;
 88      bool inq[maxv] = { 0}, known[maxv] = { 1};
 89     queue< int> Q;
 90     iter(Vect) it;
 91     inq[ 0] =  1, Q.push( 0);
 92      while(!Q.empty()){
 93         tmp = Q.front(), Q.pop(), inq[tmp] =  0;
 94         it = adj[tmp].begin();
 95          for(;it != adj[tmp].end();++it){
 96              if((*it)->vol <=  0) continue;
 97             t = dis[tmp] + (*it)->w;
 98              if(t <  0) continue;
 99              if(!known[(*it)->to] || t > dis[(*it)->to]){
100                 dis[(*it)->to] = t;
101                 known[(*it)->to] =  1;
102                 preE[(*it)->to] = *it - E;
103                 preV[(*it)->to] = tmp;
104                  if(!inq[(*it)->to])inq[(*it)->to] =  1, Q.push((*it)->to);
105             }
106         }
107     }
108     d = dis[Vnum- 1];
109      if(known[Vnum- 1]) return  1;
110      else  return  0;
111 }
112 inline  void aug( int &dis){
113      int k = Vnum -  1;
114     ans += dis;
115      while(k !=  0){
116         E[preE[k]].vol -=  1;        
117         E[preE[k]^ 1].vol +=  1;
118         k = preV[k];
119     }
120     dis =  0;
121 }
122  int main(){
123     getint(m),getint(n);
124     init();
125      int dis =  0;
126      while(spfa(dis))
127         aug(dis);
128     fprintf( out, " %d\n ",ans);
129      #if defined DEBUG
130     cout << ( double)clock() / CLOCKS_PER_SEC;
131      #endif
132      return  0;
133 }
最大费用最大流


转载于:https://www.cnblogs.com/Asm-Definer/p/4008069.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值