• UVALive


    UVa 4787

    WF题果然不一样,本来想暴力搜索,数据太大了,数组都开不了。看题解也不太懂,记录一下书上的题解,以后再看:

      此题是给出N*M的格子,有些地方是墙,不可走。求所有不能只通过向上或者向右走而走到右上角的格子。

      通过观察数据,可以发现房间的规模很大(1<=m,n<=1e6),而墙数很小(0<=w<=1000),所以可以先进行离散化。离散化的时候,采用动态规划进行处理,计算的顺序为由上而下、由右而左。设F[i][j]为格子(i,j)能否走到右上角的标志。状态转移方程:F[i][j]={false,(i,j)格为墙;F[i+1][j]||F[i][j+1],(i,j)格非墙}

      最后统计F[i][j]为false且(i,j)非墙的格子数。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 using namespace std;
      5 const int MAXN = 2010;//墙数上限
      6 struct Twall//墙的结构类型
      7 {
      8     int x1, x2, y;//行坐标为y,两个端点的列坐标在lx表的指针为x1,x2
      9 };
     10 int n, m, w, n1, m1;//房间规模为n*m,墙数为w
     11 Twall a[MAXN];//墙序列
     12 int lx[MAXN];//存储墙端点的x坐标,表长为n1
     13 long long ans;//被困的格子
     14 bool f[MAXN];//状态转移方程:当前可从lx[i]走至右上角的标志为f[i]
     15 bool g[MAXN];//g[j]=false,表明当前行第j列为被困格
     16 
     17 void init()//输入墙的信息
     18 {
     19     ans = (long long)n*m;//初始时所有格子未被困
     20     n1 = 0;//lx表长初始化
     21     for (int i = 1; i <= w; i++) {//每输入堵墙的两个端点坐标
     22         scanf("%d%d%d%d", &a[i].x1, &a[i].y, &a[i].x2, &a[i].y);
     23         ++a[i].x2;
     24         ans -= a[i].x2 - a[i].x1;//被困格子数的初始值为房间中除墙外的格子
     25         lx[++n1] = a[i].x1; lx[++n1] = a[i].x2;//存储第i墙的两个端点的x坐标
     26     }
     27     lx[++n1] = 0; lx[++n1] = n;
     28 }
     29 
     30 void discrete(int a[], int &len)//递增排序数组a,并剔去重复元素
     31 {
     32     sort(a + 1, a + 1 + len);
     33     int j = 1;
     34     for (int i = 2; i <= len; i++)
     35         if (a[j] != a[i])
     36             a[++j] = a[i];
     37     len = j;
     38 }
     39 
     40 int bin(int a[], int len, int k)//在a数组中二分查找值为k的元素下标
     41 {
     42     int l = 1, r = len, m;
     43     while (l<=r)
     44     {
     45         m = (l + r) >> 1;
     46         if (a[m] == k) return m;
     47         if (a[m] < k) l = m + 1;
     48         else r = m - 1;
     49     }
     50     return -1;
     51 }
     52 
     53 bool cmp_Twall(Twall a, Twall b)//返回同行的a堵墙在b堵墙右方,或者a堵墙在b堵墙上方的标志
     54 {
     55     if (a.y == b.y) return a.x2 > b.x2;
     56     else return a.y > b.y;
     57 }
     58 
     59 void deal_f(int k)//计算行宽为k时可通过行的格子数,调整被困的格子数
     60 {
     61     long long t = 0;
     62     for (int i = 1; i < n1; i++)//统计可通行的列宽
     63         if (f[i])                            //lx中的第i个x坐标和i+1个x坐标间为一条通向右上角的通道
     64             t += lx[i + 1] - lx[i];
     65     ans -= (long long)t*k;        //调整被困的格子数
     66 }
     67 
     68 void solve()                            //计算和输出被困的格子数
     69 {
     70     discrete(lx, n1);                //离散化:递增排序lx数组,并剔去重复元素
     71     for (int i = 1; i <= w; i++)//计算每堵墙两端的x坐标在lx中的下标位置
     72     {
     73         a[i].x1 = bin(lx, n1, a[i].x1);
     74           a[i].x2 = bin(lx, n1, a[i].x2);
     75     }
     76     sort(a + 1, a + 1 + w, cmp_Twall);//按照由右而左、由上而下排序每堵墙
     77     int last = m;                                     //从顶行出发    
     78     memset(f, 0, sizeof(f));                     //状态转移方程初始化    
     79     f[n1 - 1] = true;
     80     int st = 1, ed;                                  //从a序列的第1堵墙开始分析
     81     while (st<=w)                                  //自上而下分析每堵墙
     82     {
     83         ed = st;                                        //a[st...ed]中的墙位于同一行,且与a[ed+1]不同行
     84         while ((ed < w) && (a[ed + 1].y == a[ed].y)) ++ ed;
     85         if (a[st].y != last - 1) {                    //若当前行与前面分析的墙并非相邻行,则计算状态转移方程
     86             for (int i = n1 - 2; i >= 1; i--)
     87                 f[i] = f[i] || f[i + 1];
     88             deal_f(last - a[st].y - 1);             //累计行宽为(last-a[st].y-1)时被困的格子数
     89         }
     90         memset(g, true, sizeof(g));
     91         for (int i = st; i <= ed; i++)              //计算a[st..ed]中墙的并集,即当前行被困的格子
     92             for (int j = a[i].x1; j < a[i].x2; j++)
     93                 g[j] = false;
     94         f[n1 - 1] = f[n1 - 1] && g[n1 - 1];    //计算状态转移方程
     95         for (int i = n1 - 2; i >= 1; i--)
     96             f[i] = g[i] && (f[i] || f[i + 1]);
     97         deal_f(1);                                            //将当前行被困的格子数计入ans    
     98         last = a[st].y;                                    //记下当前的行号和下一个不同行的墙序号
     99         st = ed + 1;                                    
    100     }
    101     for (int i = n1 - 2; i >= 1; i--)                 //计算状态转移方程
    102         f[i] = f[i] || f[i + 1];
    103     deal_f(last);                                             //将最后last行中被困的格子数计入ans
    104     printf("%lld
    ", ans);                             //输出被困的格子数
    105 }
    106 
    107 int main()
    108 {
    109     int CASE = 0;                                            //测试编号初始化                
    110     while (scanf("%d%d%d",&m,&n,&w)==1,!(n==0&&m==0&&w==0))
    111     {
    112         init();                                                    //输入墙的信息    
    113         printf("Case %d: ", ++CASE);                //输出测试用例编号
    114         solve();                                                    //计算和输出被困的格子数    
    115     }
    116     return 0;
    117 }
  • 相关阅读:
    echarts曲线图
    echarts画柱状图
    echarts画环形图
    ppt素材网
    黄元御的桔梗元参汤治疗过敏性鼻炎
    vue双向数据绑定对于数组和新增对象属性不能监听的解决办法
    谈谈vue双向数据绑定问题
    一个不错的中医博客
    javascript 中 keyup、keypress和keydown事件
    浏览器渲染页面的过程
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/7389380.html
Copyright © 2020-2023  润新知