[ZOJ4127]2019ACM陕西省省赛题目笔记 #1 B - Grid with Arrows

2019ACM陕西省省赛题目笔记 #1 B题

B - Grid with Arrows ZOJ-4127


  • 第一次思考:有向 哈密顿图–>欧拉图–>半欧拉图
    • 一开始打算存每个点出度入读,最后判断 出度比入读大一的点的数量和入度比出度大一的点的数量。以上是比赛中的想法,然后发现没有考虑基图连通的问题。于是该题WA掉。

  • 第二次思考:看题解(失去梦想)
    • 题解将问题简化了,重新剖析一遍题目。得到了比半欧拉图更简单的结论(事实上我当时是有往这面思考的放弃的原因我想不起来了)
    • 题意要求要有一条经过所有点的链。因为点数与边数相同并且每个点出度不大于1。所以满足题意的图只有两种形式:
      • 1 以入度为0的点为起点有一条经过所有点的链,结尾点可指向任意处
      • 2 全图只有一个环,且该环经过所有的点。
        图示:稍后补 以后补 算了不补了

  • 代码
/* * 主线:
 *        1.存数据
 *        2.判断入度为0的点:
 *          2.1     0个 任取一点开始dfs一定能遍历所有点,否则不符题意
 *          2.2     1个 从该点开始dfs一定能遍历所有点,否则不符题意
 *          2.3     2个及以上 一定不符题意
 *        3.开始dfs判断dfs记录的最大长度length是否和点的数目相同
 *        4.数据还原
 * */
 
 //这个程序还可以简化一些完全没有必要的步骤
 #include
 #include
 #include
 #include
 #define it i*m+j   //数据保证 n*m<1e6 且长宽不定,所以就用一维数组存数据,弄一个it方便之后写
 using namespace std;
 const int N = 1e6+7;//数组下标上界

struct node{
    int in;
    int out;
}poi[N];            //存放每个点的出入度  我现在突然有了新思路:仔细一想觉得没必要 转行:
                    //可以在录数据的时候顺便判断出入度,但这样是不全面的
                    //针对不符题意的个例有点点效果,效果不大

int point[N];       //存dfs图,因为出度一定不大于1,并且不需要判断基图联通啥的,所以就这个数组足够

char map[N];        //存方向的字符串地图  题目把方向和数完全分开,所以很蛋疼。其实
                    //现在想想 其实可以都存在point[N]里面。
int vis[N+1];       //dfs标记数组,标记是否访问过

int length = 1;     //记录dfs最大长度(深度)
int k;              //点总数,用于dfs时比较

int bfs(int dep,int po) {
    vis[po] = 1;    //该点访问过
    if(!vis[point[po]]) {
        length = max(bfs(dep+1,point[po]),length);  //每个点只有至多一个后继,所以非常方便
        if(length == k) {                           //dfs出口 这步好像没有必要
            vis[po] = 0;                            //数组归零,下一次就不需要memset了
            return length;                          //这里其实是没有必要的
                                                    //我以后改,但应该不会花时间放出来
        }
    }
    vis[po] = 0;                                    //数组归零(其实相当一部分原因是因为我不大会用memset)
    return dep;
}

int main() {
    vis[N] = 1;         //这个是超出范围的指向的地方,设为1则不会被dfs到
    int T;              //测试次数
    int n,m;            //行列
    scanf("%d",&T);     //输入,我前道题前几次提交用iostream输入输出会提示TLE,换成stdio.h就好了
    while(T--) {
        cin>>n>>m;      //别在意细节=-=
        getchar();
        k = n*m;        //给全剧变量k赋值
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                scanf("%c",&map[it]);
                poi[it].in = 0;poi[it].out = 0; //初始化出度入度
            }
            getchar();
        }
        int x;
        for(int i=0;i<n;i++) {          //录入数据,更新出入度,更新后继
            for(int j=0;j<m;j++) {      //#define it i*m+j  的作用=-=
                scanf("%d",&x);
                point[it] = N;          //先赋到范围外,若实际在范围内,就改就行了
                switch(map[i*m+j]) {
                    case 'u':
                        if(i-x>=0) {
                            point[it] = (i-x)*m+j;
                            poi[it].out++;
                            poi[(i-x)*m+j].in++;
                        }
                        break;
                    case 'd':
                        if(i+x<n) {
                            point[it] = (i+x)*m+j;
                            poi[it].out++;
                            poi[(i+x)*m+j].in++;
                        }
                        break;
                    case 'l':
                        if(j-x>=0) {
                            point[it] = it-x;
                            poi[it].out++;
                            poi[it-x].in++;
                        }
                        break;
                    case 'r':
                        if(j+x<m) {
                            point[it] = it+x;
                            poi[it].out++;
                            poi[it+x].in++;
                        }
                        break;
                }
                vis[it] = 0;        //初始化vis数组,这步好像多余了=-=
            }
        }
        int start = -1;             //dfs起点 初值设为-1方便判断有几个入度为0的点
        int kk = 0;                 //有多于两个入度0的点的开关
        for(int i=0;i<n*m;i++) {
            if(poi[i].in == 0) {
                if(start == -1) {   //还没有出现入度为0的点
                    start = i;
                }else {             //出现了
                    kk = 1;         //开关打开
                    break;
                }
            }
        }
        if(start == -1) start = 0;  //如果没有入度为0的点,从任意点开始dfs
        if(!kk) {                   //没有两个及以上入度0的点
            bfs(1,start);           //开始dfs更新最大深度
            if(length == k) cout<<"Yes"<<endl;//长度(深度)和点数目相同,则符合题意
            else cout<<"No"<<endl; 
        }else {
            cout<<"No"<<endl;
        }
        length = 1;k = 0;           //恢复变量初值
    }
    return 0;
}

你可能感兴趣的:(ACM比赛补题笔记,2019ACM陕西省赛)