图论+回溯解QQ一笔画红包

博客 分享
0 292
张三
张三 2022-02-01 12:54:48
悬赏:0 积分 收藏

图论+回溯解QQ一笔画红包

[春节整活]

QQ的一笔画红包有几个特性:

1.最大为5×5的点阵,所以可以把每个点从左到右,从上到下标为1-25号点

2.每两个点只能存在一条线

3.线可以被盖住(例如连接2-1-3,2-1的线会被后来的1-3的连接线盖住),对肉眼观察很不利,但是对代码来说没有影响

 

解题思路:

1.对于线较多的点阵,可以使用邻接矩阵来画无向无权图(线较少可以使用邻接表),由于最大只有25个点,不必要考虑内存开销,所以直接使用邻接矩阵了

2.若某个节点所连接的线数为奇数,即为起点/终点,为偶数即为经过的点。若所有节点所连接的线数都为偶数,即首尾相连,任意点都可为起点/终点

3.使用回溯算法,找出将全部线只走一遍的方案,即为点阵的解

 

邻接矩阵代码如下:

/** * 邻接矩阵 */public class DenseGraph {    // 节点数    private int n;    // 边数    private int m;    // 是否为有向图    private boolean directed;    // 图的具体数据    private boolean[][] g;    //记录节点的线数量    private int[] line;    //已连接数量    private int connected;    //换行专用    private int lineFeed;        // 构造函数    public DenseGraph(){        n = 26;        m = 0;        directed = false;        // g初始化为n*n的布尔矩阵, 每一个g[i][j]均为false, 表示没有任和线        g = new boolean[n][n];        line=new int[n];    }        //初始化点阵    private void initialization(){        //临时变量,保存每个节点连接节点的数量,以判断起点        int count=0;        for (int i = 0; i < n; i++) {            for (int j = 0; j < n; j++) {                if (g[i][j]) {                    count++;                }            }            line[i]=count;            count=0;        }    }    // 返回节点个数    public int V(){ return n;}    // 返回边的个数    public int E(){ return m;}    // 向图中添加一个边    public void addEdge( int v , int w ){        assert v >= 0 && v < n ;        assert w >= 0 && w < n ;        if( hasEdge( v , w ) )            return;        g[v][w] = true;        if( !directed )            g[w][v] = true;        m ++;    }    // 验证图中是否有从v到w的边    boolean hasEdge( int v , int w ){        assert v >= 0 && v < n ;        assert w >= 0 && w < n ;        return g[v][w];    }        //开始运行    public void start(){        //初始化        this.initialization();        //输出邻接矩阵        this.print();        //寻找起点        int qsd=-1;        for (int i = 0; i < line.length; i++) {            if (line[i]%2!=0){                qsd=i;                break;            }else if (line[i]!=0){                qsd=i;            }        }        //从起点开始回溯寻找路线        flashBack(qsd);    }    public boolean flashBack(int a){        //如果已经走过的线数量等于总线数量,说明寻路完成        if (connected==m){            System.out.print("路线:["+a+"] -> ");            return true;        }else {            //遍历此点与全部节点的关系并按照以下执行            //1.如果两点之间有连接,假设此路线为正确路线,将此线改变为无连接,并开始从此点遍历            //2.如果最终无法走过全部线,则确定此路线不正确,回溯并将此线还原为连接,继续遍历            for (int i = 0; i < n; i++) {                if (g[a][i]){                    g[a][i]=false;                    g[i][a]=false;                    connected++;                    if (flashBack(i)){                        lineFeed++;                        if (lineFeed%20==0)                            System.out.println();                        System.out.print("["+a+"] -> ");                        return true;                    }else {                        connected--;                        g[a][i]=true;                        g[i][a]=true;                    }                }            }            return false;        }    }    //输出邻接矩阵    public void print(){        System.out.println("边共:"+m+"条");        System.out.print("邻接矩阵 ");        for (int i = 1; i < n; i++) {            System.out.print(i+"   \t");        }        System.out.println();        for (int i = 1; i < n; i++) {            System.out.print(i+" \t");            for (int j = 1; j < n; j++) {                System.out.print(g[i][j]+" \t");            }            System.out.println();        }    }}

使用方法:

创建对象(new)

添加边(addEdge)

开始运行(start)

 

对于线非常多的图,一条一条添加线依然很麻烦,有没有更好的办法呢?

posted @ 2022-02-01 12:18 unIlIl 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员