Akari问题详细解答

28 篇文章 0 订阅

1,问题:

1,问题描述:

Akari 问题

Akari问题有时又被称为Light up或者Beleuchtung,源于日本逻辑解密游戏系列Nikoli,同属于Nikoli谜题的除Akari之外还有Sudoku(数独)和Kakuro(数谜)等。

游戏规则很简单。点灯游戏的棋盘是一张方形格网,其中的格子可能是黑色也可能是白色。游戏目标是在格网中放置灯泡,使之能照亮所有的白色方格。如果一个方格所在的同一行或同一列有一个灯泡,并且方格和灯泡之间没有黑色格子阻挡,那么这个方格将被灯泡照亮。同时,放置的每个灯泡不能被另一个灯泡照亮。
某些黑色格子中标有数字。这些数字表示在该格子四周相邻的格子中共有多少个灯泡。

2,题目要求:

格式说明

输入数据由若干文件组成,每个文件描述一个Akari问题的初始状态,编写程序读入此文件,根据初始状态求解。有的有一个解,有的有多个解,我们保证有解。

文件由若干行组成,第一行为两个整数 nm,代表棋盘的行数和列数。之后的 n 行每行有 m 个整数表示棋盘的每个格子的状态,若它为 -2,则表示是白格子,若它为 -1,则表示是没有数字的黑格子,若它为 0-4,则表示是数字 0-4 的黑格子。若你想把灯泡放在白色格子上面,则需要将 -2 改为 5,因为 5表示有灯泡的格子。


2,算法

2.1:algo1:

问题和算法你都可以从https://www.educoder.net/tasks/mthkqlu46583获得;

如下:

对问题进行划分
根据黑色方格中数字的大小,按从大到小的顺序进行排序,并将“在一个有数字的黑色方格周围放置‘车’”定义为一步。
选择枚举
枚举每一步的选择。
若黑色方格中的数字为4,则其相邻的周围格子的“灯泡”的放置方式为1种,即上下左右均放置一个“灯泡”。
若黑色方格中的数字为3,则其相邻的周围格子的“灯泡”的放置方式为4种,即在左右下、下右上、右上左、上左下的格子中放置“灯泡”。
若黑色方格中的数字为2,则其相邻的周围格子的“灯泡”的放置方式有6种,即在上左、上下、上右、左下、左右、下右的格子中放置“灯泡”。
若黑色方格中的数字为1,则其相邻的周围格子的“灯泡”的放置方式有4种,即在上、下、左、右的格子中放置“灯泡”。
若黑色方格中的数字为0,则其相邻的周围格子的“灯泡”的放置方式有1种,即所有周围格子均不放置车。
构造解空间
根据上述讨论构造解空间,初始状态为解空间树的根节点,从编号最大的黑色格子开始尝试填入“灯泡”,填入后判断是否为一个可行解,若为可行解,则解空间向下进行分枝,否则向上进行回溯。解空间树的结构大致如图2所示。
程序设计与实现
根据回溯法的伪代码和上述算法的设计思路,建立合适的数据模型与程序结构,编写程序求解问题,同时记录程序的运行时间。
讨论与改进
分析算法的时间负责度与空间复杂度,根据某一用例和计算机的计算能力,估计程序运行时间,并将该时间与实际运行时间进行比较。
同时讨论与优化程序结构和数据结构,以求达到更快的程序执行速度和更少的内存占用量。

2.2:algo2:

   // 根据curm的有效位来获得可以放灯的所有位置
    // arg1: 存放可以放灯的所有位置
    // arg2: 当前矩阵
    void getLeft(vector<POINT>& left, vector<vector<grid> > & curm){


    // 根据当前放入的那个灯来更新矩阵,同时检测是否可以成功更新
    // 不能成功更新的说名当前点不能放,curm所作的改变需要清除,则返回false然后换下一个点来回溯
    bool updateCurmByLight(POINT light, vector<vector<grid> > & curm) {



    //回溯函数
    void backT(vector<POINT>& left, vector<vector<grid> > & curm,\
                                                          vector<vector<int> > & res){
        if(如果能cover住所有的位置,则将res更新,然后更新findRes){

            return ;
        }

        //
        if(当所有的位置都用了,但是没得到结果,也要回溯){

            return ;
        }

        for(int i=0; i<left.size() &&(!findRes); i++){
            auto tmpCurm= curm;
            auto tmpLightedUp = lightedUp;
            int tmpDarkSum=darkSum;
            auto tmpleft = left;

            if(left.size() >= 15){
                cout<<"  left len: "<<left.size()<<endl;
            }

            if(updateCurmByLight(left[i], curm)){ //可以正确更新,并且内容已经被更新
                //更新剩余,放入该灯,接着回溯
                getLeft(left, curm);
                backT(left, curm, res);
            }

            //当该点的回溯退出来了,我们需要回滚到进入之前,然后进入到下一个遍历;
            curm = tmpCurm;
            darkSum = tmpDarkSum;
            lightedUp = tmpLightedUp;
            left = tmpleft;
        }
    }

    vector<vector<int> > solveAkari(vector<vector<int> > & g){

        //存放结果
        vector<vector<int> > res(n, vector<int>(m, 0));

        //初始化你的可以放灯的位置 调用函数: getLeft
        getLeft(left, curm);

        //回溯获得结果
        backT(left, curm, res);


        return res;
    }
}


第二种是自己写的,算法是每次放一个灯,而第一种是每次放一个黑格子周围的灯们(比如黑格子数字为3,则有4中放法,是这样去遍历回溯的)

3,解答代码

这里的解答代码很详细,但是采用的是算法中的第2种,可以直接阅读,注释很足;

//
// Created by cxy on 19-7-22.
//
# include <bits/stdc++.h>
# include "akari.h"
using namespace std;

/**
输入数据由若干文件组成,每个文件描述一个Akari问题的初始状态,编写程序读入此文件,
根据初始状态求解。有的有一个解,有的有多个解,我们保证有解。文件由若干行组成,

第一行为两个整数 n,m,代表棋盘的行数和列数。
之后的 n 行每行有 m 个整数表示棋盘的每个格子的状态,

若它为 -2,则表示是白格子,
若它为 -1,则表示是没有数字的黑格子,
若它为 0-4,则表示是数字 0-4 的黑格子。
若你想把灯泡放在白色格子上面,则需要将 -2 改为 5,因为 5 表示有灯泡的格子。

你需要在右侧代码编辑框给出的函数中编写你的代码,函数的参数为我们给出的light up矩阵,
你需要在该函数中返回相同大小的结果矩阵。对于有多个解的light up,你可以返回其中的
任意一组解,我们将对你返回的矩阵进行检查,若结果正确,提示The answer is right!,
否则提示其它。

*/

namespace aka{
//请在命名空间内编写代码,否则后果自负

    //用以记录灯的位置
    typedef struct{
        int x = 0;
        int y = 0;
        int val = -2;
    }POINT;

    //为棋盘里的每个格子加一个标志位
    typedef struct{
        int value = 0;
        bool valid = true;
    }grid;

    //找到结果的标志
    bool findRes = false;

    //没有被照亮的总数
    int darkSum = 0;

    //黑色格子的坐标
    vector<POINT> bs;

    //n行m列
    int n = 0, m = 0;

    //标记矩阵: 白格子是否在黑格子周围,是白格子并且在黑格子周围,则为true,否则为false
    vector<vector<bool> >aroundBlack;
    //存放: 在黑点周围的格子围绕的黑点的数值;
    vector<vector<int> >aroundBlackValue;
    //获得剩余的灯的时候按照围绕的黑格子的数字从高到低排列;
    bool cmp(POINT a, POINT b){
        return a.val > b.val;
    }
//    struct {
//        bool operator()(int a, int b) const
//        {
//            return a < b;
//        }
//    } customLess;
    //表记是否被照亮
    vector<vector<bool> >lightedUp;

    void display(vector<vector<grid> > & ans)
    {
        printf("your magic matrix--------------------------------> \n");
        int n = ans.size(), m = ans[0].size();
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                printf("%4d", ans[i][j].value);
            }
            printf("\n");
        }
        printf("your valid matrix \n");

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                printf("%4d", ans[i][j].valid);
            }
            printf("\n");
        }

//        printf("your aroundBlack \n");
//
//        for (int i = 0; i < n; ++i) {
//            for (int j = 0; j < m; ++j) {
//                printf("%4d", aroundBlack[i][j]?1:0);
//            }
//            printf("\n");
//        }

    }

    void pv(vector<POINT> a){
        printf("left is \n");

        for (int j = 0; j < a.size(); ++j) {
            printf("%4d", a[j].x);
        }printf("\n");

        for (int j = 0; j < a.size(); ++j) {
            printf("%4d", a[j].y);
        }printf("\n");

        for (int j = 0; j < a.size(); ++j) {
            printf("%4d", a[j].val);
        }printf("\n");
    }

    void pv1(vector<vector<int> > a){
        printf("around black 's value is \n");

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                printf("%4d", a[i][j]);
            }
            printf("\n");
        }
    }

    void pv2(vector<vector<bool> > a){
        printf("lightedUp is \n");

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                printf("%4d", a[i][j]?1:0);
            }
            printf("\n");
        }
    }

    // 根据curm的有效位来获得可以放灯的所有位置
    // arg1: 存放可以放灯的所有位置
    // arg2: 当前矩阵
    void getLeft(vector<POINT>& left, vector<vector<grid> > & curm){
        //扫描矩阵, ,需要先把格子周围的点放入left的前面,
        // 不在黑格子周围的放在后面,以保证每次回溯总是从黑格子周围开始;
        left.clear();
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                POINT tmp;
                if(curm[i][j].valid){ //说明当前格子有效
                    tmp.x = i;
                    tmp.y = j;
                    //接下来要区分它是不是黑格子周围的
                    if(aroundBlack[i][j]){ //从头放入
                        //该可以用点周围的黑色灯的值
                        tmp.val = aroundBlackValue[i][j];
                        left.emplace(left.begin(), tmp);
                    }else{ //从尾巴放入
                        left.push_back(tmp);
                    }
                }
            }
        }
        sort(left.begin(), left.end(), cmp);
    }

    //设置i,j处的点,要是为0,将其i周围置为false
    void setAround0(int i,  int j, vector<vector<grid> > & curm){
        if(curm[i][j].value == 0){
            if(i==0){
                if(j==0){
                    curm[i+1][j].valid = false;curm[i][j+1].valid = false;
                }else if(j==m-1){
                    curm[i][j-1].valid = false;curm[i+1][j].valid = false;
                }else{
                    curm[i][j+1].valid = false;
                    curm[i][j-1].valid = false;
                    curm[i+1][j].valid = false;
                }
            }else if(i==n-1){
                if(j==0){
                    curm[i-1][j].valid = false;curm[i][j+1].valid = false;
                }else if(j==m-1){
                    curm[i-1][j].valid = false;curm[i][j-1].valid = false;
                }else{
                    curm[i-1][j].valid = false;
                    curm[i][j+1].valid = false;
                    curm[i][j-1].valid = false;
                }
            }else{
                if(j==0){
                    curm[i][j+1].valid = false;
                    curm[i-1][j].valid = false;
                    curm[i+1][j].valid = false;
                }else if(j==m-1){
                    curm[i][j-1].valid = false;
                    curm[i-1][j].valid = false;
                    curm[i+1][j].valid = false;
                }else{
                    curm[i+1][j].valid = false;curm[i][j+1].valid = false;
                    curm[i-1][j].valid = false;curm[i][j-1].valid = false;
                }
            }
        }
    }


    // 根据当前放入的那个灯来更新矩阵,同时检测是否可以成功更新
    // 不能成功更新的说名当前点不能放,curm所作的改变需要清除,则返回false然后换下一个点来回溯
    bool updateCurmByLight(POINT light, vector<vector<grid> > & curm) {
        int x0 = light.x, y0= light.y;
        //判断当前点是否有效
        if(!curm[x0][y0].valid){
            return false;
        }
        //放入该灯,当然本身也需要算被照亮,可以只darkSum-1:
        curm[x0][y0].value = 5;
        curm[x0][y0].valid = false;
        darkSum--;

        //放入灯以后,该处周围的黑格子的数字需要减少,并且查看是否有为零的情况,为0的话需要将其周围视作不可用
        vector<POINT> blacks;
        if(y0 >= 1 && (1<=curm[x0][y0-1].value && 4>=curm[x0][y0-1].value)){
            curm[x0][y0-1].value --;

            setAround0(x0, y0-1, curm);
        }
        if(x0 >= 1 && (1<=curm[x0-1][y0].value && 4>=curm[x0-1][y0].value)){
            curm[x0-1][y0].value --;

            setAround0(x0-1, y0, curm);
        }
        if(y0 <= n-2 && (1<=curm[x0][y0+1].value && 4>=curm[x0][y0+1].value)){
            curm[x0][y0+1].value --;

            setAround0(x0, y0+1, curm);
        }
        if(x0 <= m-2 && (1<=curm[x0+1][y0].value && 4>=curm[x0+1][y0].value)){
            curm[x0+1][y0].value --;

            setAround0(x0+1, y0, curm);
        }


        //分别从该灯的+x,-x,+y,-y方向更新被照亮的地方,也就是将其置为fasle
        int xp=light.x, xn=xp;
        int yp=light.y, yn=yp;
        //curm 为黑色格子的方式为-1到4,5是灯,所以只要当前格子值不是-2,就需要停止
        //而且当为5的时候需要返回false,当遇到[-1, 4]之间的数据就停止探索;
        //x正方向
        for(int i=xp+1; i<m ;i++){
            if(-1<=curm[i][y0].value && 4>=curm[i][y0].value){
                break;
            }else if(curm[i][y0].value == 5){
                return false;
            }else{
                //被照亮 若是i没有被照亮,那么就将darkSum减1
                if(!lightedUp[i][y0]){
                    curm[i][y0].valid = false;
                    darkSum--;
                    lightedUp[i][y0] = true;
                }
            }
        }
        //x负方向
        for(int i=xp-1; i>=0 ;i--){
            if(-1<=curm[i][y0].value && 4>=curm[i][y0].value){
                break;
            }else if(curm[i][y0].value == 5){
                return false;
            }else{
                //被照亮 若是i没有被照亮,那么就将darkSum减1
                if(!lightedUp[i][y0]){
                    curm[i][y0].valid = false;
                    darkSum--;
                    lightedUp[i][y0] = true;
                }
            }
        }
        //y正方向
        for(int i=yp+1; i<n ;i++){
            if(-1<=curm[x0][i].value && 4>=curm[x0][i].value){
                break;
            }else if(curm[x0][i].value == 5){
                return false;
            }else{
                //若是i没有被照亮,那么就将darkSum减1, 同时置valid和照亮居正
                if(!lightedUp[x0][i]){
                    curm[x0][i].valid = false;
                    darkSum--;
                    lightedUp[x0][i] = true;
                }
            }
        }
        //y负方向
        for(int i=yn-1; i>=0 ;i--){
            if(-1<=curm[x0][i].value && 4>=curm[x0][i].value){
                break;
            }else if(curm[x0][i].value == 5){
                return false;
            }else{
                //若是i没有被照亮,那么就将darkSum减1, 同时置valid和照亮居正
                if(!lightedUp[x0][i]){
                    curm[x0][i].valid = false;
                    darkSum--;
                    lightedUp[x0][i] = true;
                }
            }
        }
        return true;
    }


    //回溯函数
    void backT(vector<POINT>& left, vector<vector<grid> > & curm,\
                                                          vector<vector<int> > & res){
        //返回触发条件:
        //1,失败放入,在update的时候就解决了
        // 也就是说,update完成后,会决定这个点是放入还是不放入,你不需要关心
        //2,找到了解答
        //如果发现当前的灯们的顶点(也就是最近放入的一个灯)不能放入,则直接返回

        //如果能cover住所有的位置,则将res更新,然后更新findRes
        if(darkSum == 0){
            bool satisfy = true;
            //如果不能满足所有黑色格子上的数字都变为0
            for(int i=0; i<bs.size(); i++){
                satisfy = satisfy && (curm[bs[i].x][bs[i].y].value == 0);
            }
            //那就返回
            if(!satisfy){
                return ;
            }
            for(int i=0; i<n; i++){
                for(int j=0; j<m; j++){
                    res[i][j] = curm[i][j].value;
                }
            }
            findRes = true;
            exit(0);
            return ;
        }

        //当所有的位置都用了,但是没得到结果,也要回溯
        if(left.empty()){
//            cout <<endl<<endl<< "backTrack to last backT's next loop"<<endl<<endl;
            return ;
        }
        //在调用backT后,记得把left,lights和curm退回到押入之前的状态
        //这个left是在你选定了一个黑格子作为起点,把它周围的点放到left的最前面,
        // 相当于你这个for循环只是在遍历l以该黑格子周围放灯作为起点,就可以得到最终结果
        // 所以初始化left的时候,需要先把黑格子周围的点放入left的前面,不在黑格子周围的放在后面,以保证每次回溯总是从前面开始;
#pragma omp parallel for
        for(int i=0; i<left.size() &&(!findRes); i++){
            auto tmpCurm= curm;
            auto tmpLightedUp = lightedUp;
            int tmpDarkSum=darkSum;
            auto tmpleft = left;
            //>>>>>>>>>>>>>打印调试信息>>>>>>>>>>>>>>>>
//            display(curm);
//            pv(left);
//            pv2(lightedUp);
//            cout<<"--------dark: "<<darkSum<<" | ready to using x:"<<left[i].x<<", y: "<<left[i].y<<"  left len: "<<left.size()<<endl;
            if(left.size() >= 15){
                cout<<"  left len: "<<left.size()<<endl;
            }

            if(updateCurmByLight(left[i], curm)){ //可以正确更新,并且内容已经被更新
                //更新剩余,放入该灯,接着回溯
                getLeft(left, curm);
                backT(left, curm, res);
            }

            //当该点的回溯退出来了,我们需要回滚到进入之前,然后进入到下一个遍历;
            curm = tmpCurm;
            darkSum = tmpDarkSum;
            lightedUp = tmpLightedUp;
            left = tmpleft;
        }
    }

    vector<vector<int> > solveAkari(vector<vector<int> > & g){
        // 请在此函数内返回最后求得的结果

        //获得n行m列
        n = g.size();
        m = g[0].size();


        //根据当前矩阵获得的剩下可以使用的灯
        vector<POINT> left(0);

        //根据回溯法一步步改变的当前矩阵
        vector<vector<grid> > curm(0);

        //下一步,初始化这个ABlack,然后将其赋值给aroundBlack即可
        //标记矩阵: 白格子是否在黑格子周围,是白格子并且在黑格子周围,则为true,否则为false
        //点亮矩阵: lightUp标记该格子是否被照亮
        vector<vector<bool> > ABlack(n, vector<bool>(m, false));
        vector<vector<int> > ABlackValue(n, vector<int>(m, false));
        vector<vector<bool> > LUp(n, vector<bool>(m, false));

        //初始化当前灯的矩阵和每个位置上的有效位,顺便初始化darkSum,顺便获得aroundBlack的值
        for(int i=0; i<n; i++) {
            curm.emplace_back(vector<grid>(m));
        }
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                curm[i][j].value = g[i][j];
                if(g[i][j] != -2){  //说明是黑色格子,标记周围的白格子,他们是在黑色周围,标记数字为0的黑格子的周围无效
                    //将自己置为fasle
                    curm[i][j].valid = false;
                    //需要将数字为0的黑格子周围的点置为false
                    setAround0(i, j, curm);
                    //标记自己周围的格子为aroundBlack
                    if(i==0){
                        if(j==0){
                            ABlack[i+1][j] = true;ABlack[i][j+1] = true;
                            ABlackValue[i+1][j] = curm[i][j].value;
                            ABlackValue[i][j+1] = curm[i][j].value;
                        }else if(j==m-1){
                            ABlack[i][j-1] = true;ABlack[i+1][j] = true;
                            ABlackValue[i][j-1] = curm[i][j].value;
                            ABlackValue[i+1][j] = curm[i][j].value;
                        }else{
                            ABlack[i][j+1] = true;
                            ABlack[i][j-1] = true;
                            ABlack[i+1][j] = true;
                            ABlackValue[i][j+1] = curm[i][j].value;
                            ABlackValue[i][j-1] = curm[i][j].value;
                            ABlackValue[i+1][j] = curm[i][j].value;
                        }
                    }else if(i==n-1){
                        if(j==0){
                            ABlack[i-1][j] = true;ABlack[i][j+1] = true;
                            ABlackValue[i][j+1] = curm[i][j].value;
                            ABlackValue[i-1][j] = curm[i][j].value;
                        }else if(j==m-1){
                            ABlack[i-1][j] = true;ABlack[i][j-1] = true;
                            ABlackValue[i-1][j] = curm[i][j].value;
                            ABlackValue[i][j-1] = curm[i][j].value;
                        }else{
                            ABlack[i-1][j] = true;
                            ABlack[i][j+1] = true;
                            ABlack[i][j-1] = true;
                            ABlackValue[i-1][j] = curm[i][j].value;
                            ABlackValue[i][j+1] = curm[i][j].value;
                            ABlackValue[i][j-1] = curm[i][j].value;
                        }
                    }else{
                        if(j==0){
                            ABlack[i][j+1] = true;
                            ABlack[i-1][j] = true;
                            ABlack[i+1][j] = true;
                            ABlackValue[i][j+1] = curm[i][j].value;
                            ABlackValue[i-1][j] = curm[i][j].value;
                            ABlackValue[i+1][j] = curm[i][j].value;
                        }else if(j==m-1){
                            ABlack[i][j-1] = true;
                            ABlack[i-1][j] = true;
                            ABlack[i+1][j] = true;
                            ABlackValue[i][j-1] = curm[i][j].value;
                            ABlackValue[i-1][j] = curm[i][j].value;
                            ABlackValue[i+1][j] = curm[i][j].value;
                        }else{
                            ABlack[i+1][j] = true;ABlack[i][j+1] = true;
                            ABlack[i-1][j] = true;ABlack[i][j-1] = true;
                            ABlackValue[i][j-1] = curm[i][j].value;
                            ABlackValue[i][j+1] = curm[i][j].value;
                            ABlackValue[i+1][j] = curm[i][j].value;
                            ABlackValue[i-1][j] = curm[i][j].value;
                        }
                    }
                }else{ //说明是白格子,一开始当然没有被照亮
                    darkSum ++;
                    LUp[i][j] = false;
                }
            }
        }
        aroundBlack = ABlack;
        aroundBlackValue = ABlackValue;
        lightedUp = LUp;

        //获得初始的bs
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(1<=curm[i][j].value && 4>=curm[i][j].value){
                    POINT tmp;
                    tmp.x = i; tmp.y = j;
                    bs.emplace_back(tmp);
                }
            }
        }

        //>>>>>>>>>>>>>打印调试信息>>>>>>>>>>>>>>>>
//            display(curm);
//            pv(left);
            pv2(aroundBlack);
        pv1(aroundBlackValue);
        cout<<"-----------------------------";

        //初始化你的可以放灯的位置 调用函数: getLeft

        //存放结果
        vector<vector<int> > res(n, vector<int>(m, 0));

        //调用backT函数获得结果
        getLeft(left, curm);

        //通过并行的优化,就是在这里,我们通过每一个可以开始的节点来分别开启线程,这样做时间应该会快一些;
        backT(left, curm, res);

        //由于这res里面的数字为1,到4的灯在寻找答案的时候都被毁掉了,所以现在做一个恢复:
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(1<=g[i][j] && 4>=g[i][j]){
                    res[i][j] = g[i][j];
                }
            }
        }
        return res;
    }
}


//问题:
//darksum的更新被重复了,也就是当

//问题:
//需要有一个变量,记录全局的黑色格子边上剩余的个数,要是不为0,那么即使获得答案也不被认可

//现在是在剩余黑格子旁边可以使用的总数非零的情况下获得答案,显然是不行的;


//获得所有的黑点的points,去扫描直到所有都为0,
// 采用abNum 不能判断是否满足了所有的黑点周围都有正确的点

 

 

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值