2018-03-10 刷题

1087 All roads 最短路径算法,所有的最短路径,各种回溯

这是我最近写过的最丑的代码,没有之一。太繁琐,变量名都起不过来,只能加注释,顾不上那么多了。发现所有 test case 都过了的时候我简直惊呆了。

在dijkstra最短路算法的基础上,对于路径一样长的情况都要存下来。

然后就可以递归回溯各种可能的路径,寻找happiness最大的或者平均happiness最大的。这里

# include 
# include 

int N, K; //number of cities, number of roads
int dest;
int happy[200];
int cost[200][200];
char name[200][4];

int p[200][200]; //id of the city's parent
int np[200]; //number of parents
int dist[200]; // total cost of coming 
bool status[200]; //if its shortest dist is known

int nRoute = 0; //有多少条路线是最短路
int h, mh; //当前路线的happy, 最优路线的happy
int nNode, mnNode; //当前经历的节点,最优路线的节点数
int route[200], mRoute[200]; //当前路线,最优路线

int id(char *str) {
    for (int i = 0; i < N; i++) {
        if (strcmp(str, name[i]) == 0) {
            return i;
        }
    }
    return -1;
}

void read() {
    scanf("%d %d %s", &N, &K, name[0]); //start point at 0
    for (int i = 1; i < N; i++) {
        scanf("%s %d", name[i], happy + i);
    }
    dest = id("ROM");

    //init the graph
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cost[i][j] = 1 << 30;
        }
        cost[i][i] = 0;
        dist[i] = (1 << 30);
    }

    for (int i = 0; i < K; i++) {
        char a1[4], a2[4];
        int d, ia1, ia2;
        scanf("%s %s %d", a1, a2, &d);
        ia1 = id(a1); ia2 = id(a2);
        cost[ia1][ia2] = (cost[ia2][ia1] = d);
    }
}

void dijk() {
    dist[0] = 0;
    while (1) {
        int m = 0, md = 1 << 30; //m = 最短路径未知的集合里距离最小的点
        for (int i = 0; i < N; i++) {
            if (!status[i] && dist[i] < md) {
                md = dist[i];
                m = i;
            }
        }
        if (m == 0 && status[0]) { //说明所有的点 status 都为真了,所以m才没变
            break;
        }

        status[m] = true; // 最短路已知
        for (int i = 0; i < N; i++) {
            if (!status[i] && cost[m][i] < (1 << 30)) { //有路
                if (cost[m][i] + dist[m] < dist[i]) { //比之前的路更短
                    np[i] = 1;
                    p[i][0] = m;
                    dist[i] = cost[m][i] + dist[m];
                }
                else if (cost[m][i] + dist[m] == dist[i]) { //和之前的路一样短
                    p[i][(np[i] ++)] = m;
                }
            }
        }
    }
}

void backward(int x) {
    route[nNode++] = x;
    h += happy[x];
    if (x == 0) { //回溯到了起点
        nRoute++;
        if (h > mh || (h == mh && (h / (float)nNode) > (mh / (float)mnNode))) { //更好的路线
            mh = h;
            mnNode = nNode;
            memcpy(mRoute, route, sizeof(int) * 200);
        }
    }
    else {
        for (int i = 0; i < np[x]; i++) { //i的每一个祖先
            backward(p[x][i]);
        }
    }
    h -= happy[x];
    nNode--;
}

int main() {
    read();
    dijk();

    backward(dest); 

    printf("%d %d %d %d\n", nRoute, dist[dest], mh, mh / (mnNode - 1));
    for (int i = mnNode - 1; i >= 1; i--) {
        printf("%s->", name[mRoute[i]]);
    }
    printf("%s", name[mRoute[0]]);

    return 0;
}

1086. Tree Traversals Again 中序遍历的逆向,后序遍历

给了一堆做中序遍历时的 push 和 pop 的命令,要求输出后续遍历。

先从操作逆推了一棵树。写中序遍历的时候,对左孩子入栈,一直到没有左孩子为止,然后出栈后找右孩子。因此找到了规律,如果操作是 push,那下一步准备插入的位置就是它的左子树(如果下一步不是 push 就算了);如果 pop 了一个,那么下一步准备插入的位置是它的右子树。逻辑想了半天,写起来很简单。

恰好节点的 key 都是 1-N的,我就不开树了,直接拿几个数组存下来,免得还得根据 key 查找节点的位置,多麻烦。

最后做一个后序遍历,很简单,过了。

1085. Perfect Sequence

很简单的逻辑,简单到我有点不会做了,卡了半天。

1084. Broken Keyboard

也很简单,按照顺序比对字符就可以了。注意指针怎么向后挪。记一下缺少的键是否输出过了。

1083. List Grades 过于简单

结构体,排个序,输出。本来以为肯定超时,结果case也很弱……

1082 read number in Chinese 复杂逻辑

给出不多于9位的数字,要输出汉语读法。刚开始的思路是每4位读一下,最后结合到一起,结果发现逻辑太复杂了,前面0,后面0,中间两个0,写得很晕。

最终的写法是,先识别不同位置的 0,该跳过还是读一次。标记完 0 之后就很简单了,打表输出。贴代码如下:

# include 
# include 

char digits[10][6] = { "ling ", "yi ", "er ", "san ", "si ", "wu ", "liu ", "qi " , "ba ", "jiu " };
char pos[9][6] = { "", "Qian ", "Bai ", "Shi ", "", "Qian ", "Bai ", "Shi ", "" };
char result[9][20];
char num[11];
int len;

int main() {
    scanf("%s", num);
    if (num[0] == '-') {
        printf("Fu ");
        for (int i = 0; num[i] != '\0'; i++) {
            num[i] = num[i + 1];
        }
    }
    len = strlen(num);

    int i = 0;
    while (num[i] == '0' && i < len) { //刚开始的0要跳过
        num[i] = 's'; //skip
        i++;
    }
    int start = i; //除去0后数字真正开始的地方
    i = len - 1;
    while (num[i] == '0' && i >= 0) { //最后的0也跳过
        num[i] = 's';
        i--;
    }
    i = len - 5;
    while (num[i] == '0' && i >= 0) { //万位及之前相邻的跳过
        num[i] = 's';
        i--;
    }
    i = 0;
    while (i < len) { //其它地方连0都读一个0
        if (num[i] == '0') {
            num[i] = 'r'; //read
            i++;
            while (num[i] == '0' && i < len) {
                num[i] = 's';
                i++;
            }
        }
        i++;
    }

    char read[200] = "";
    for (i = 0; i < len; i++) {
        if (num[i] >= '1' && num[i] <= '9') {
            strcat(read, digits[num[i] - '0']);
            strcat(read, pos[i + 9 - len]);
        }
        else if (num[i] == 'r') {
            strcat(read, digits[0]);
        }
        if (i >= start && i + 9 - len == 0) { //到了亿位
            strcat(read, "Yi ");
        }
        if (i >= start && i + 9 - len == 4) { //到了万位
            strcat(read, "Wan ");
        }
    }
    
    if (strlen(read) == 0) { //输入的是0
        strcpy(read, "ling");
    }
    else {
        read[strlen(read) - 1] = '\0';
    }
    

    printf("%s", read);
    return 0;
}

//Fu yi ba er jiu san Yi si Qian wu Bai liu Shi qi Wan ba Qian jiu Bai
//Fu yi Yi er Qian san Bai si Shi wu Wan liu Qian qi Bai ba Shi jiu

1081. Rational Sum 简单

和 1088 的要求很相似,甚至还变简单了。刚开始跑出了浮点错误,后来看论坛发现是 gcd() 里面没有判断有 0 的情况,有 0 直接返回 1 就可以了。

1079. Total Sales of Supply Chain 超时

题目很简单,一棵树,最后需要一些节点的深度。刚开始我是用 bfs 把所有节点的深度记了一遍,发现超时严重;后来改成需要哪个的深度,就递归往爹娘求,一直求到根节点。结果还是有一个case超时。我把后面需要用的 pow(x, y) 打表了一遍,那个case还是超时。

你可能感兴趣的:(2018-03-10 刷题)