zoukankan      html  css  js  c++  java
  • [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)

     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 ",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优化……醉了醉了)

      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 ",ans);
    129     #if defined DEBUG
    130     cout << (double)clock() / CLOCKS_PER_SEC;
    131     #endif
    132     return 0;
    133 }
    最大费用最大流


  • 相关阅读:
    java中的subList
    值传递和引用传递
    java程序中有异常处理和没有异常处理的区别
    正则表达式
    poj 3187 三角数问题
    poj 2718 切数问题 穷竭搜索
    ACM 广度优化搜索算法总结
    poj 3669 火星撞地球问题 bfs算法
    poj 2251 三维地图最短路径问题 bfs算法
    ACM 深度优化搜索算法小总结
  • 原文地址:https://www.cnblogs.com/Asm-Definer/p/4008069.html
Copyright © 2011-2022 走看看