• 三角形灯阵 拓扑排序


    题目描述:

    中秋节的晚上,小x在桌面上放了许多好看的彩灯。遗憾的是,这些彩灯可能并非 全部都亮着。于是,小x打算把全部这些彩灯都点亮。
    但是,小x很快发现,这些彩灯的摆放是非常有规律的,事实上,彩灯的位置都在平面的 正三角形镶嵌的某个交点处。距离为单位长度的彩灯被认为相互相邻。可以看出,每个彩灯 最多与六个彩灯相邻,相邻的彩灯都在以其为中心的单位正六边形的顶点上。
    下图就是一种合法的彩灯摆放(对应样例数据):

    其中,实心和空心的圆点分别代表亮与灭的彩灯,实线边代表彩灯间的相邻关系。
    图中的边组成了若干单位正三角形(边长为单位长度的三角形),可以看出,每个彩灯 与它相邻的彩灯最多可以组成六个单位正三角形。而所有的单位三角形可被分成两类,我们 称为 A 类以及 B 类,图中加阴影的三角形属于 A 类。A 类三角形的特征是,三个顶点分别 在上方、左下方及右下方。
    每个 A 类三角形中有一个开关(图中未画出),按动三角形中的开关 开关会改变三个顶点上每个彩灯的亮灭状态(亮变成灭,灭变成亮)。
    那么,要点亮所有彩灯,最少需要按动多少次开关呢?

    输入格式

    输入数据描述了每个彩灯的初始亮灭状态以及所有三角形。

    第一行一个整数 N,表示彩灯的数目,彩灯被编号为 1 至 N。

    第二行 N 个 0 或 1 的整数,第 i 个 0 或 1 表示第 i 个彩灯初始的状态是灭或亮。 第三行一个整数 M,表示有 M 对彩灯相邻的关系。

    以下 M 行,每行三个整数,表示一对彩灯相邻的关系。前两个整数即相邻的这一对彩灯的编号。第三个整数表示第二个彩灯相对于第一个彩灯的方位,0~5 分别表示右方、右下 、 左下、左方、左上、右上。

    例如,“1 3 1”表示“第 3 个彩灯在第 1 个彩灯的右下方”。

    输出格式

    如果不可能做到点亮所有彩灯,输出-1,否则输出最小的步数。

    寒假在海亮好像听过刘老师农夫山泉过这个题目,当时都没怎么听,看见机房大佬Chdy再写,于是也加入进来,发现是个很有意思的题目;

    因为原来有印象类似的题目 所以看了一眼 发现是个拓扑;

    然后我们考虑怎么建图 我们知道 拓扑维护的是一个依赖关系;

    对于这个题 我们呢考虑怎么改变状态 才能使整个彩灯点亮;

    我们肯定是首先寻找一个点,然后向其它点拓展,当便利一边所有的点后,我们找到了最少步数并且点亮了所有的彩灯;

    对于一个A类三角形的顶点,如果他并不是其他A类三角形的左下右下的点,我们考虑从他开始,那么如果他是亮着的;

    我们是不能改变他所在的这个三角形的亮暗的,因为如果改变了,那么当前这个亮着的就会暗,而向下传递关系时我们不能让已经遍历过的点再次暗掉;

    所以只有这个点是暗着的,我们便找了唯一的方案,所以就可以将和他相连的点状态取反,如果有另一个点成为了当前这个顶点的状态,

    即没有三角形将它作为左下右下点,我们接下来从这个点开始,直至将所有的点遍历完;

    那么我们考虑对于一个点,我们改变他的状态,对左边右边的点是没有影响的,所以我们只考虑斜上斜下的点;

    所以对与一个点x,如果y位于它的左上右上,我们将y指向x,x的入度++;

    如果位于右下左下,我们将x指向y,y的入度++;

    开始拓扑就解决了这个问题;

    至于其它种解法,比如fzh大佬的将A类三角行中间加入一个点,然后连边进行拓扑,写起来麻烦,但他自己要说比较巧妙,那好吧 嗯。。真巧妙!!%%%

    #include<bits/stdc++.h>
    using namespace std;
    const int N=50000;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
        while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
        x*=f;
    }
    int n,m,x,y,z,tot,ans,lin[N<<1],s[N],in[N]; 
    struct gg {
        int y,next;
    }a[N<<1];
    inline void add(int x,int y) {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    int main() {
        //freopen("1.in","r",stdin);
        read(n);
        for(int i=1;i<=n;i++) {
            read(s[i]);
        }
        read(m);
        for(int i=1;i<=m;i++) {
            read(x); read(y); read(z);
            if(z==1||z==2) add(x,y),in[y]++;
            if(z==4||z==5) add(y,x),in[x]++;
        }
        queue<int>q; 
        for(int i=1;i<=n;i++) {
            if(!in[i]) q.push(i);
        }
        ans=0;
        while(q.size()) {
            int x=q.front();q.pop();
            for(int i=lin[x];i;i=a[i].next){
                int y=a[i].y;
                in[y]--;
                if(!in[y]) q.push(y);
                if(!s[x]) s[y]^=1;
            }
            if(!s[x]) ans++;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    100个高质量的photoshop画笔
    VC调用DLL库方法的方法
    VC6中使用CHtmlView在对话框控制中显示HTML
    CtrlList 排序问题。
    VC ADO使用说明
    VC右键弹出菜单的实现
    VC6工程项目文件说明
    VC6中用DOM遍历网页中的元素
    C/C++头文件一览
    最常见的20种VC++编译错误信息
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11458569.html
Copyright © 2020-2023  润新知