zoukankan      html  css  js  c++  java
  • [NOIP2010] 引水入城 贪心 + 记忆化搜索

    ~~~题面~~~

    题解:

    本蒟蒻并没有想到bfs的做法,,,,

    只会dfs了

    首先我们需要知道一个性质。

    我们设k[i].l 为在i点建立水库可以支援到的最左边的城市,k[i].r为最右边的。

    那么点i可以支援到的城市必然有且仅有k[i].l ~ k[i].r这整个区间。

    这是为什么呢?

    其实我也不会证明,但是可以大致说明一下,也不知道是否严谨。。。。

    观察上图我们可以发现,假设我们可以到达 l 和 r ,却无法到达中间那个,那么说明mid要高于 l 和 r,

    通过观察我随意画的几组路线可以发现,如果mid无法通过上方到达,但我们显然是有机会去上方的,而我们没有去。这说明上面也堵住了!

    那么这说明什么?

    说明mid根本无法到达啊。

    所以我们在dfs找 l 和 r 的时候,直接记录哪些城市是已经到达的,dfs完后扫一遍,看是否有城市无法到达,如果有,那么说明我们找到的 l 和 r 是不对的,同时说明任务无法完成,于是按题意输出即可

    否则就贪心的做线段覆盖。

    同时注意dfs需要记忆化(也相当于DP了)

    先把区间按照 l 为第一优先级,r为第二优先级排序,然后在l可以触碰到当前位置的时候,优先选择r靠后的。

    这个的正确性是显然的。

    因此我们就可以得到答案了

    代码中我懒得想,所以用暴力处理了一下n = 1的情况。。。。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 510
      5 #define inf 10000000
      6 #define getchar() *o++
      7 #define D printf("line in %d
    ", __LINE__);
      8 char READ[5001000], *o = READ;
      9 int n, m, tot;
     10 int s[AC][AC], a[5] = {0, 0, -1, 1}, b[5] = {-1, 1, 0, 0};
     11 bool z[AC * AC], vis[AC];
     12 
     13 struct node{
     14     int l, r;
     15 }k[AC * AC];
     16 
     17 inline int read()
     18 {
     19     int x = 0; char c = getchar();
     20     while(c > '9' || c < '0') c = getchar();
     21     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     22     return x; 
     23 }
     24 
     25 void pre()
     26 {
     27     n = read(), m = read();
     28     for(R i = 1; i <= n; i++)
     29         for(R j = 1; j <= m; j++) s[i][j] = read(); 
     30 }
     31 
     32 inline int get(int x, int y)
     33 {
     34     return (x - 1) * m + y;
     35 }
     36 
     37 node dfs(int x, int y)
     38 {
     39     int _x, _y, id;node tmp, now;
     40     id = get(x, y);
     41     if(z[id]) return k[id];
     42     tmp.l = inf, tmp.r = 0;
     43     if(x == n) 
     44     {
     45         vis[y] = true;
     46         tmp.l = tmp.r = y;
     47     }
     48     z[id] = true;
     49     for(R i = 0; i <= 3; i++)
     50     {
     51         _x = x + a[i], _y = y + b[i];
     52         if(s[_x][_y] >= s[x][y]) continue;
     53         if(_x > 0 && _x <= n && _y > 0 && _y <= m)
     54         { 
     55             now = dfs(_x, _y);
     56             if(now.l < tmp.l) tmp.l = now.l;
     57             if(now.r > tmp.r) tmp.r = now.r;
     58         }        
     59     } 
     60     k[id] = tmp;
     61     return tmp;
     62 }
     63 
     64 inline bool cmp(node a, node b)
     65 {
     66     if(a.l != b.l) return a.l < b.l;
     67     else return a.r < b.r;
     68 }
     69 
     70 void work()
     71 {
     72     int tmp = 0;
     73     for(R i = 1; i <= m; i++) if(!vis[i]) ++tmp;//error!!!是m啊
     74     if(tmp)
     75     {
     76         printf("0
    %d
    ", tmp);
     77         exit(0);
     78     }
     79     int ans = 0, l = 1;//记录当前使用段数 + 
     80     sort(k + 1, k + m + 1, cmp);
     81     //for(R i = 1; i <= m; i++)//只能用第一行
     82     int i = 1;
     83     while(1)
     84     {//error 之前那样写会死循环的。。。。
     85         //printf("%d
    ", i);
     86         tmp = 0;//记录最远能到哪里
     87         while(k[i].l <= l + 1 && i <= m) 
     88         {
     89             if(k[i].r > tmp) tmp = k[i].r;
     90             ++i;
     91         }
     92         l = tmp, ++ans;
     93         if(l == m) break;
     94     }
     95     printf("1
    %d
    ", ans);
     96 }
     97 
     98 void special()//如果沙漠城市临海
     99 {
    100     int ans = 0, cnt = 0, tmp = 1, l, r;
    101     while(tmp < m && s[1][tmp + 1] < s[1][tmp]) ++tmp;
    102     k[++cnt].l = 1, k[cnt].r = tmp;
    103     tmp = m;
    104     while(tmp > 1 && s[1][tmp] > s[1][tmp - 1]) --tmp;
    105     k[++cnt].l = tmp, k[cnt].r = m; 
    106     for(R i = 2; i < m; i++)//因为只有一行,最多就500个城市,所以暴力即可
    107     {
    108         if(s[1][i] >= s[1][i - 1] && s[1][i] >= s[1][i + 1])
    109         {
    110             l = r = i;
    111             while(r < m && s[1][r + 1] < s[1][r]) ++r;
    112             while(l > 1 && s[1][l - 1] < s[1][l]) --l;
    113             k[++cnt] = (node) {l, r};
    114         }
    115     }
    116     l = 1;
    117     sort(k + 1, k + m + 1, cmp);
    118     int i = 1;
    119     while(1)
    120     {//error 之前那样写会死循环的。。。。
    121         //printf("%d
    ", i);
    122         tmp = 0;//记录最远能到哪里
    123         while(k[i].l <= l + 1 && i <= m) 
    124         {
    125             if(k[i].r > tmp) tmp = k[i].r;
    126             ++i;
    127         }
    128         l = tmp, ++ans;
    129         if(l == m) break;
    130     }
    131     printf("1
    %d
    ", ans);
    132 }
    133 
    134 int main()
    135 {
    136     freopen("in.in", "r", stdin);
    137     fread(READ, 1, 5000000, stdin);
    138     pre();
    139     if(n == 1) 
    140     {
    141         special();
    142         exit(0);
    143     }
    144     for(R i = 1; i <= m; i++) dfs(1, i);
    145     work();
    146     //printf("time used ... %lf
    ", (double) clock()/CLOCKS_PER_SEC);
    147     fclose(stdin);
    148     return 0;
    149 }
  • 相关阅读:
    codeforces 672B B. Different is Good(水题)
    codeforces 672A A. Summer Camp(水题)
    poj-1273 Drainage Ditches(最大流基础题)
    hdu-3592 World Exhibition(差分约束)
    poj-1201 Intervals(差分约束)
    解决Windows只能打英文输入法图标不见不显示问题
    Windows查看MD5码
    Windows中类似 linux netstat grep命令
    清空所有账户回收站
    PLSQL dev字符集乱码设置
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9348697.html
Copyright © 2011-2022 走看看