您的位置:首页 > 其它

算法竞赛入门经典(第二版)-刘汝佳-第四章 函数与递归 例题+习题(15/16)

2016-02-19 22:43 555 查看

说明

本文是我对第四章题目的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。

另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第四章contest

如果想直接看某道题,请点开目录后点开相应的题目!!!

例题

例4-1 UVA 1339 古老的密码

思路

只需要分别统计两个字符串中26个字母出现的个数,然后对统计数组做一个排序,如果一样则结果为YES。

详细解释见书P73。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main(void)
{
    int n[2], cnt[2][26];
    char s[2][101];
    while (cin >> s[0] >> s[1]) {
        memset(cnt, 0, sizeof(cnt));
        for (int i = 0; i < 2; i ++) {
            n[i] = strlen(s[i]);
            for (int j = 0; j < n[i]; j ++)
                cnt[i][s[i][j]-'A'] ++;
            sort(cnt[i], cnt[i]+26);
        }

        bool flag = true;
        for (int j = 0; j < 26; j ++)
            if (cnt[0][j] != cnt[1][j]) flag = false;
        printf("%s\n", flag ? "YES" : "NO");
    }

    return 0;
}


例4-2 UVA 489 刽子手游戏

思路

给定两个字符串,第一个串是用来匹配的,从第二个串的第一个字符开始匹配,如果第二个串中的字符在第一个串出现,则表示猜中了,第一个串中的相同的那个字符都算被猜中;如果没有出现则表示猜错,同样的猜错只算一次。在整个匹配的过程中,如果在还没猜错7次之前,第一个串中所有的字符都被猜完了, 则输出“You win.”,如果你还没全部猜完的时候就已经猜错7次,则输出“You lose.”。如果整个匹配过程结束后,你没赢也没输,则输出“You chickened out.”。

这个题需要注意的细节很多,我WA了多次。在这里提供一组测试数据吧,希望对一直WA的同学有用。

测试数据来源:uva489讨论

INPUT:

1

cheese

chese

2

cheese

abcdefg

3

cheese

abcdefgij

4

rommel

romlnptuyq

5

rommel

romlnptuyqw

6

casa

ca

7

otorrinolaringologia

otr

8

peru

abcdefghijklmno

9

lastima

la

10

aaaaaaaaaaaaaaaaaaaaaa

a

11

bobobobobobobo

b

12

lalalalabababababaaaaa

alhhhhhhhhhhhhhhhhhhhhhhh

13

lkjaskljfkjklsalsdjfslkjfjf

dfklsdfskld

14

nbmbmmbnbbmbmbmbmnbnbnbmbmmbbnbnnbmbmbmboffiifififififfiif

ppppppppppppppppppppppppppppppppppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweeeeeeeeeeeeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrrrrrrb

15

abcdefggegegegegegege

gegegegegddabacecevbbdbdnndnenjejje

16

diccionariosdeportugues

dcptgiowqqqaazzxxxx

17

b

c

18

kljfdsjfoieoijefnvnenvionewveinvewv

dkdjjshue

19

ooooooooooooopppppppppppppppppppppeeeeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwtttttttttttttttttt

operwt

20

ooooooooooooopppppppppppppppppppppeeeeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwtttttttttttttttttt

operwqzxcvbnmklo

21

ploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploploplo

jslkfsdjfsjfljfsdjfkdsjkjflkf

22

añoañoañoañoañoañoañoañoañoañoañoañoañoañoañoperuperuperuperuperuperuperuperuperuañoañoañoañoañoañoañoañoañoaño

añoperu

23

añoañoañoañoañoañoañoañoañoañoañoañoañoañoañoperuperuperuperuperuperuperuperuperuañoañoañoañoañoañoañoañoañoaño

ududududuudududupepeppewguqwhuihewhepqwehuwehwmncvmnnvmnvcnb

4

t

r

1

aaa

bcdbcdbcdbcdefghja

7

aaa

bcdbcdbcdbcdefdta

2

aaa

bcdbcdbcdbcdegt

1

cheese

ch

1

z

abcdef

1

aaa

bcdbcdbcdbcdefghja

7

aaa

bcdbcdbcdbcdefdta

2

aaa

bcdbcdbcdbcdegt

167

axyq

eprxibexxyf

171

nho

tonpyzwotkg

1

z

abcdef

1

abcdef

aghijklmcdefb

2

abcdef

abcdklmnoegfi

3

abcdef

abcklmnopdf

-1

OUTPUT:

Round 1

You win.

Round 2

You chickened out.

Round 3

You lose.

Round 4

You chickened out.

Round 5

You lose.

Round 6

You chickened out.

Round 7

You chickened out.

Round 8

You lose.

Round 9

You chickened out.

Round 10

You win.

Round 11

You chickened out.

Round 12

You chickened out.

Round 13

You chickened out.

Round 14

You chickened out.

Round 15

You chickened out.

Round 16

You chickened out.

Round 17

You chickened out.

Round 18

You chickened out.

Round 19

You win.

Round 20

You lose.

Round 21

You chickened out.

Round 22

You win.

Round 23

You lose.

Round 4

You chickened out.

Round 1

You lose.

Round 7

You win.

Round 2

You chickened out.

Round 1

You chickened out.

Round 1

You chickened out.

Round 1

You lose.

Round 7

You win.

Round 2

You chickened out.

Round 167

You chickened out.

Round 171

You lose.

Round 1

You chickened out.

Round 1

You lose.

Round 2

You win.

Round 3

You chickened out.

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main(void)
{
    int rnd;
    int used[26];
    char s[1000];
    while (cin >> rnd && rnd != -1) {
        memset(used, 0, sizeof(used));
        scanf("%s", s);
        int len = strlen(s);
        for (int i = 0; s[i]; i ++)
            used[s[i]-'a'] ++;

        int wrong = 0;
        scanf("%s", s);
        for (int i = 0; s[i]; i ++) {
            if (used[s[i]-'a']) {
                len -= used[s[i]-'a'];
                used[s[i]-'a'] = 0;
            } else
                wrong ++;
            if (wrong == 7 || len == 0) break; 
        }

        printf("Round %d\n", rnd);
        if (wrong == 7)
            printf("You lose.\n");
        else if (len <= 0)
            printf("You win.\n");
        else
            printf("You chickened out.\n");
    }

    return 0;
}


例4-3 UVA 133 发放救济金

思路

这个题没什么复杂度,就只是模拟。注意go函数可以把顺时针和逆时针统一起来的巧妙方法。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, k, m;
int person[20];

int go(int s, int d, int p)
{
    while (d--) {
        do {
            s = (s+p+n) % n;
        } while (!person[s]);
    }
    return s;
}

int main(void)
{
    while (cin >> n >> k >> m, n || k || m) {
        memset(person, 1, sizeof(person));
        int left = n;
        int a = n-1, b = 0;
        while (left) {
            a = go(a, k, 1);
            b = go(b, m, -1);
            if (left < n) printf(",");
            printf("%3d", a+1);
            person[a] = 0;
            left --;
            if (person) {
                printf("%3d", b+1);
                person[b] = 0;
                left --;
            }
            if (left == 0) break;
        }
        printf("\n");
    }

    return 0;
}


例4-4 UVA 213 信息解码

[b]思路


主要考察两个知识点:getchar()读入,以及二进制与十进制的互相转换。

同时,频繁使用的或者模块性很强的功能写成函数,的确能使代码精简很多。

代码

[code]#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 7;
const int M = 1<<(N+1);

char head[M];
int bi[N+1];

void init()
{
    bi[1] = 0;
    int fact = 1;
    for (int i = 1; i < 7; i ++) {
        fact <<= 1;
        bi[i+1] = bi[i] + fact - 1;
    }
}

int get01()
{
    char c;
    while ((c = getchar()) == '\n');
    return c-'0';
}

int getMsg(int len)
{
    int res = 0;
    while (len --)
        res = 2*res + get01();
    return res;
}

char decode(int msg, int len)
{
    return head[bi[len]+msg];
}

int main(void)
{
    init();
    while (gets(head)) {
        int len;
        while (len = getMsg(3)) {
            int msg;
            while ((msg = getMsg(len)) != ((1<<len)-1))
                putchar(decode(msg, len));
        }
        putchar('\n');
        getchar();
    }

    return 0;
}


例4-5 UVA 512 追踪电子表格中的单元格

思路

我总觉得书中给出的第一种解法时间复杂度不好。因为每次删除或增加行列,后面所有的行列都要跟着动,这样每个操作的复杂度是O(n^2)。

我是用链表来做的,将使用的行(列)的id建立一个顺序链表,其它未使用(或删除)的id分别放到行(列)的队列中,如果插入就取出一个id来用。

但这样导致我写的代码很长,而且确实比较复杂,尽管原题的范例都通过了,但提交后一直WA。目前这是我第四章唯一没有AC的题。等有时间再重新看这个题吧。

先贴上WA代码。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int N = 51;

int n, m;
int data[N+1][N+1];
int next[2][N+1];
int pos[N+1][N+1];
priority_queue<int, vector<int>, greater<int> > pq[2];

void init_data()
{
    memset(next, 0, sizeof(next));
    for (int i = 0; i < 2; i++)
        while (pq[i].size()) pq[i].pop();
    memset(data, 0, sizeof(0));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            data[i][j] = i*N+j;
    for (int i = 1; i <= n; i++)
        next[0][i-1] = i;
    for (int i = n+1; i <= N; i++)
        pq[0].push(i);
    for (int j = 1; j <= m; j++)
        next[1][j-1] = j;
    for (int j = m+1; j <= N; j++)
        pq[1].push(j);
}

int id(int t, int k)
{
    int u = 0;
    while (k--) u = next[t][u];
    return u;
}

void exchange(int ax, int ay, int bx, int by)
{
    swap(data[id(0, ax)][id(1, ay)], data[id(0, bx)][id(1, by)]);
}

void ins_or_del(char op, int t, vector<int> num)
{
    sort(num.begin(), num.end());
    int u = 0, un = next[t][u];
    int k = 0, i = 0;
    while (un) {
        k++;
        if (num[i] == k) {
            if (op == 'D') {
                next[t][u] = next[t][un];
                pq[t].push(un);
                int h = next[1-t][0];
                while (h) {
                    if (t == 0) data[un][h] = 0;
                    else data[h][un] = 0;
                    h = next[1-t][h];
                }
            } else {
                int j = pq[t].top();
                pq[t].pop();
                next[t][u] = j;
                next[t][j] = un;
                u = un;
            }
            i++;
            if (i == num.size()) break;
        } else
            u = next[t][u];
        un = next[t][un];
    }
    int x = (op == 'D') ? -1 : 1;
    if (t == 0) n += x*num.size();
    else m += x*num.size();
}

void print()
{
    printf("==========\n");
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            int x = data[id(0, i)][id(1, j)];
            printf("%d,%d  ", x/N, x%N);
        }
        printf("\n"); 
    }
}   

void prase_pos()
{       
    vector<int> ids[2];
    for (int i = 0; i < 2; i++) {
        int u = next[i][0];
        while (u) { 
            ids[i].push_back(u);
            u = next[i][u];
        }           
    }               

    memset(pos, 0, sizeof(pos));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            int x = data[ids[0][i-1]][ids[1][j-1]];
            if (x) pos[x/N][x%N] = i*N+j;
        }       
    }       
}           

int main(void)
{           
    int kase = 0;
    int t, k, j;
    char op[3]; 
    int ax, ay, bx, by;
    while (scanf("%d%d", &n, &m), n || m) {
        init_data();
        scanf("%d", &t);
        for (int i = 0; i < t; i++) {
            scanf("%s", op);
            if (op[0] == 'E') {
                scanf("%d%d%d%d", &ax, &ay, &bx, &by);
                exchange(ax, ay, bx, by);
            } else {
                scanf("%d", &k);
                vector<int> num;
                while (k--) {
                    scanf("%d", &j);
                    num.push_back(j);
                }
                int type = (op[1] == 'R') ? 0 : 1;
                ins_or_del(op[0], type, num);
            }
            //print();
        }

        prase_pos();
        scanf("%d", &t);
        if (++kase > 1) printf("\n");
        printf("Spreadsheet #%d\n", kase);
        for (int i = 0; i < t; i++) {
            scanf("%d%d", &ax, &ay);
            printf("Cell data in (%d,%d) ", ax, ay);
            int p = pos[ax][ay];
            if (p) printf("moved to (%d,%d)\n", p/N, p%N);
            else printf("GONE\n");
        }
    }

    return 0;
}


例4-6 UVA 12412 师兄帮帮忙

思路

这个题实在不想ACM题,纯粹模拟,小细节特别多。不过我竟然只WA了一次就过了,很庆幸啊!

由于需要频繁的插入删除操作,我采用双向链表作为主要数据结构。这里的链表不是数据结构书中常用的结构体链表,而是数组链表。具体做法就是开一个大结构体数组,用到的节点对应的数组下标插入链表中,未使用的下标放在queue中,插入节点时从queue中取id,删除时将id回收到queue中。这样做的好处是内存少,时间复杂度低。

打印全部信息的函数可以用循环写的更规范一些,详见代码。

需要注意的几个细节有:

1、精度的控制,注意加EPS

2、人数为0时平均分输出为0

3、搜索学号只可能有一个,姓名则可能有多个

代码

[code]#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

const int N = 100;
const double EPS = 1e-5;

struct Stu {
    char sid[11];
    int cid;
    char name[11];
    int score[4];
    int total;
};

Stu stus[N+1];
int n;
int pre[N+1], next[N+1];
queue<int> ID;

const char *msg[] = {"", "Please enter the SID, CID, name and four scores. Enter 0 to finish.",
"Please enter SID or name. Enter 0 to finish.",
"Please enter SID or name. Enter 0 to finish.",
"Showing the ranklist hurts students' self-esteem. Don't do that.", ""};

void print_menu()
{
    puts("Welcome to Student Performance Management System (SPMS).\n");
    puts("1 - Add");
    puts("2 - Remove");
    puts("3 - Query");
    puts("4 - Show ranking");
    puts("5 - Show Statistics");
    puts("0 - Exit\n");
}

void init()
{
    n = 0;
    memset(pre, 0, sizeof(pre));
    memset(next, 0, sizeof(next));
    for (int i = 1; i <= N; i++)
        ID.push(i);
}

int search_sid(char sid[11])
{
    int u = next[0];
    while (u) {
        if (strcmp(stus[u].sid, sid) == 0) return u;
        u = next[u];
    }
    return 0;
}

void search_name(vector<int>& ids, char name[11])
{
    int u = next[0];
    while (u) {
        if (strcmp(stus[u].name, name) == 0) ids.push_back(u);
        u = next[u];
    }
}

void add()
{
    char sid[11];
    puts(msg[1]);
    while (scanf("%s", sid) && strcmp(sid, "0")) {
        int id = ID.front();
        ID.pop();
        Stu& s = stus[id];
        strcpy(s.sid, sid);
        scanf("%d%s", &s.cid, s.name);
        s.total = 0;
        for (int i = 0; i < 4; i++) {
          scanf("%d", &s.score[i]);
          s.total += s.score[i];
        }
        if (search_sid(sid))
          puts("Duplicated SID.");
        else {
            next[id] = next[0];
            next[0] = id;
            pre[id] = 0;
            pre[next[id]] = id;
            n++;
        }
        puts(msg[1]);
    }
}

void remove()
{
    char s[11];
    puts(msg[2]);
    while (scanf("%s", s) && strcmp(s, "0")) {
        int id;
        vector<int> ids;
        int cnt = 0;
        if (id = search_sid(s)) {
            next[pre[id]] = next[id];
            pre[next[id]] = pre[id];
            ID.push(id);
            cnt = 1;
        } else {
            search_name(ids, s);
            if (cnt = ids.size()) {
                for (int i = 0; i < cnt; i++) {
                    id = ids[i];
                    ID.push(id);
                    next[pre[id]] = next[id];
                    pre[next[id]] = pre[id];
                }
            }
        }
        n -= cnt;
        printf("%d student(s) removed.\n", cnt);
        puts(msg[2]);
    }
}

int get_rank(int score)
{
    int u = next[0];
    int rank = 1;
    while (u) {
        if (stus[u].total > score) rank++;
        u = next[u];
    }
    return rank;
}

void print_stu(int id)
{
    Stu &s = stus[id];
    printf("%d %s %d %s", get_rank(s.total), s.sid, s.cid, s.name);
    for (int i = 0; i < 4; i++)
        printf(" %d", s.score[i]);
    printf(" %d %.2lf\n", s.total, s.total/4.0+EPS);
}

void query()
{
    char s[11];
    puts(msg[3]);
    while (scanf("%s", s) && strcmp(s, "0")) {
        int id;
        vector<int> ids;
        int cnt = 0;
        if (id = search_sid(s)) {
            print_stu(id);
        } else {
            search_name(ids, s);
            if (cnt = ids.size()) {
                for (int i = cnt-1; i >= 0; i--)
                  print_stu(ids[i]);
            }
        }
        puts(msg[3]);
    }
}

void show_rank()
{
    puts(msg[4]);
}

const char *courses[] = {"Chinese", "Mathematics", "English", "Programming"};

void show_stat()
{
    int cid = 0;
    printf("Please enter class ID, 0 for the whole statistics.\n");
    scanf("%d", &cid);

    int num = 0, total[4] = {0}, passed1[4] = {0}, passed2[5] = {0};
    int u = next[0];
    while (u) {
        Stu& s = stus[u];
        if (cid == 0 || s.cid == cid) {
            num++;
            int cnt = 0;
            for (int i = 0; i < 4; i++) {
                total[i] += s.score[i];
                passed1[i] += (s.score[i]/60);
                cnt += (s.score[i]/60);
            }
            passed2[cnt]++;
        }
        u = next[u];
    }
    for (int i = 0; i < 4; i++) {
        puts(courses[i]);
        printf("Average Score: %.2lf\n", num ? (double)(total[i])/num+EPS : 0);
        printf("Number of passed students: %d\n", passed1[i]);
        printf("Number of failed students: %d\n\n", num-passed1[i]);
    }
    puts("Overall:");
    for (int i = 4; i >= 1; i--) {
        if (i == 4) printf("Number of students who passed all subjects: ");
        else {
            printf("Number of students who passed %d or more subjects: ", i);
            passed2[i] += passed2[i+1];
        }
        printf("%d\n", passed2[i]);
    }
    printf("Number of students who failed all subjects: %d\n\n", passed2[0]);
}

int main()
{
    int cmd;
    print_menu();
    init();
    while (scanf("%d", &cmd) && cmd) {
        switch(cmd) {
            case 1:
                add();
                break;
            case 2:
                remove();
                break;
            case 3:
                query();
                break;
            case 4:
                show_rank();
                break;
            case 5:
                show_stat();
                break;
            case 0:
            default:
                return 0;
        }
        print_menu();
    }

    return 0;
}


习题

习4-1 UVA 1589 象棋

思路

我的思路是考察所有黑方的将下一步的位置是否会被红方的子吃掉。检查的时候可以根据棋盘,也可以根据棋子。事实上后一种复杂度应该略低一些,因为红方最多有7个子。

另外有几个小技巧:

1、蹩马腿的判断可以用数组实现,详见代码。

2、棋子车和炮是否将军的判断可以根据棋子与将之间棋子的个数来判断,车与将之间为0,或炮与将之间为1都是将军。

即使示例能过,这个题也很可能WA。我在这个题上没有注意到的地方有:

1、黑方的将可能吃掉红方的子。因而需要实现保存上一步的棋盘。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct Chess {
    char t; //0, 1, G, R, H, C
    int x, y;
    Chess () {}
    Chess (char t1, int x1, int y1)
    {
        t = t1, x = x1, y = y1;
    }
};

char tab0[11][11], tab[11][11];
Chess gnr;
int n;
Chess pos[8];
int mov[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int movH[8][2] = {{1, 2}, {-1, 2}, {2, 1}, {2, -1},
    {1, -2}, {-1, -2}, {-2, 1}, {-2, -1}};

bool legal(int x, int y)
{
    return 1 <= x && x <= 3 && 4 <= y && y <= 6;
}

Chess move(Chess p, int i)
{
    Chess p1(p.t, p.x+ mov[i][0], p.y + mov[i][1]);
    if (!legal(p1.x, p1.y)) return Chess('0', 1, 1);
    tab[p1.x][p1.y] = '1';
    tab[p.x][p.y] = '0';
    return p1;
}

int cnt(Chess p1, Chess p2)
{
    int cnt = 0;
    if (p1.x == p2.x) {
        int add = (p1.y < p2.y ? 1 : -1);
        for (int j = p1.y+add; j != p2.y; j += add) {
            if (tab[p1.x][j] != '0') cnt ++;
        }
    }
    else if (p1.y == p2.y) {
        int add = (p1.x < p2.x ? 1 : -1);
        for (int j = p1.x+add; j != p2.x; j += add) {
            if (tab[j][p1.y] != '0') cnt ++;
        }
    } else
        cnt = 10;
    return cnt;
}

bool check()
{
    //true 合法, false 将死
    for (int i = 1; i <= n; i ++) {
        //printf("%c : %d %d %d %d\n", pos[i].t, pos[i].x, pos[i].y, pos[0].x, pos[0].y);
        if (pos[0].x == pos[i].x && pos[0].y == pos[i].y) continue;
        if (pos[i].t == 'G' || pos[i].t == 'R') {
            if (cnt(pos[0], pos[i]) == 0) return false;
        } else if (pos[i].t == 'C') {
            if (cnt(pos[0], pos[i]) == 1) return false;
        } else {
            for (int j = 0; j < 8; j ++) {
                if (pos[i].x + movH[j][0] == pos[0].x
                && pos[i].y + movH[j][1] == pos[0].y
                && tab[pos[i].x + mov[j/2][0]][pos[i].y + mov[j/2][1]] == '0')
                    return false;
            }
        }
    }
    return true;
}

void print()
{
    for (int i = 1; i <= 10; i ++) {
        for (int j = 1; j <= 9; j ++) {
            printf("%c ", tab0[i][j]);
        }
        printf("\n");
    }       
}       

int main(void)
{       
    while (scanf("%d%d%d", &n, &gnr.x, &gnr.y), n || gnr.x || gnr.y) {
        memset(tab0, '0', sizeof(tab0)); 
        tab0[gnr.x][gnr.y] = '1';
        for (int i = 1; i <= n; i ++) {
            char type[2];
            scanf("%s%d%d", type, &pos[i].x, &pos[i].y);
            pos[i].t = type[0];
            tab0[pos[i].x][pos[i].y] = pos[i].t;
        }
        //print();

        bool res = true; 
        for (int i = 0; i < 4; i ++) {
            memcpy(tab, tab0, sizeof(tab0)); 
            pos[0].x = gnr.x+ mov[i][0];
            pos[0].y = gnr.y + mov[i][1]; 
            if (!legal(pos[0].x, pos[0].y)) continue;
            tab[pos[0].x][pos[0].y] = '1';
            tab[gnr.x][gnr.y] = '0';
            if (check()) { res = false; break; }
        }       

        if (res) puts("YES");
        else puts("NO");
    }       

    return 0;
}


习4-2 UVA 201 正方形

思路

直接暴力搜索所有可能的正方形。此题数据较弱,如果数据强,可能需要用更好的方法。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 9;

int n;
bool a

[2];
int num
;

int check(int i, int j, int s)
{
    int flag = 1;
    for (int x = i; x < i+s; x ++)
        if (!a[x][j][1] || !a[x][j+s][1]) flag = 0;
    for (int y = j; y < j+s; y ++)
        if (!a[i][y][0] || !a[i+s][y][0]) flag = 0;
    return flag;
}

int main(void)
{
    int m, t = 0;
    while (scanf("%d", &n) != EOF) {
        cin >> m;
        memset(a, 0, sizeof(a));
        char c[2];
        int x, y;
        for (int i = 0; i < m; i ++) {
            scanf("%s%d%d", c, &x, &y);
            if (c[0] == 'H') a[x-1][y-1][0] = 1;
            else a[y-1][x-1][1] = 1;
        }

        if (t > 0) printf("\n**********************************\n\n");
        printf("Problem #%d\n\n", ++t);
        memset(num, 0, sizeof(num));
        bool flag = false;
        for (int s = 1; s < n; s ++) {
            for (int i = 0; i < n-s; i ++) {
                for (int j = 0; j < n-s; j ++) {
                    num[s] += check(i, j, s);
                }
            }
            if (num[s]) {
                flag = true;
                printf("%d square (s) of size %d\n", num[s], s);
            }
        }
        if (flag == false) printf("No completed squares can be found.\n");
    }

    return 0;
}


习4-3 UVA 220 黑白翻转棋

思路

把示例数据过了紧接着提交就AC了。模拟题注意细节就好。只有真正自己写出来才能体会。

代码

[code]#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef pair<int, int> P;

char s[10][10];
char c;
int d[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1},
    {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};

bool legal(int x, int y)
{
    return 1 <= x && x <= 8 && 1 <= y && y <= 8;
}

char next()
{
    return c == 'W' ? 'B' : 'W';
}

bool check_dir(int x, int y, int i)
{
    if (s[x][y] != '-') return false;
    int nx = x + d[i][0];
    int ny = y + d[i][1];
    if (!legal(nx, ny) || s[nx][ny] != next())
        return false;
    while (true) {
        nx += d[i][0];
        ny += d[i][1];
        if (!legal(nx, ny) || s[nx][ny] == '-')
            return false;
        if (s[nx][ny] == c) return true;
    }
}
bool can_set(int x, int y)
{
    for (int i = 0; i < 8; i ++) {
        if (check_dir(x, y, i)) {
            return true;
        }
    }
    return false;
}

void set(int x, int y)
{
    for (int i = 0; i < 8; i ++) {
        if (check_dir(x, y, i)) {
            int nx = x+d[i][0], ny = y+d[i][1];
            while (s[nx][ny] == next()) {
                s[nx][ny] = c;
                nx += d[i][0];
                ny += d[i][1];
            }
        }
    }
    s[x][y] = c;
}

P cnt()
{
    P res(0, 0);
    for (int i = 1; i <= 8; i ++) {
        for (int j = 1; j <= 8; j ++) {
            if (s[i][j] == 'B') res.first ++;
            if (s[i][j] == 'W') res.second ++;
        }
    }
    return res;
}

void print_chess()
{
    for (int i = 1; i <= 8; i ++) {
        for (int j = 1; j <= 8; j ++) {
            printf("%c", s[i][j]);
        }
        printf("\n");
    }
}

int main(void)
{   
    int kase;
    char op[4];
    cin >> kase;
    for (int t = 1; t <= kase; t++) {
        for (int i = 1; i <= 8; i ++)
            scanf("%s", s[i]+1);
        scanf("%s", op);
        c = op[0];

        if (t > 1) printf("\n");
        while (scanf("%s", op)) {
            if (op[0] == 'L') {
                bool flag = false;
                bool first = true;
                for (int i = 1; i <= 8; i ++) {
                    for (int j = 1; j <= 8; j ++) {
                        if (can_set(i, j)) {
                            flag = true;
                            if (!first) printf(" ");
                            printf("(%d,%d)", i, j);
                            first = false;
                        } 
                    }
                }
                if (flag == false) printf("No legal move.");
                printf("\n");
            } else if(op[0] == 'M') {
                int x = op[1]-'0', y = op[2]-'0';
                if (can_set(x, y)) { set(x, y);}
                else { c = next(); set(x, y); }
                c = next();
                P num = cnt();
                printf("Black - %2d White - %2d\n", num.first, num.second);
            } else {
                print_chess();
                break;
            }
        }
    }

    return 0;
}


习4-4 UVA 253 骰子涂色

思路

判断等价,需要遍历所有状态。我这里定义了两种操作,一是往右转1/4圈roll_right,一是朝前的面往上转1/4圈roll_up。这两种操作足够遍历所有状态。

具体遍历操作使用函数check_all_right_roll循环实现。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 10000;

int n, a
;

void roll_right(char s[7])
{
    char c = s[1];
    s[1] = s[2];
    s[2] = s[4];
    s[4] = s[3];
    s[3] = c;
}

void roll_up(char s[7])
{
    char c = s[1];
    s[1] = s[5];
    s[5] = s[4];
    s[4] = s[0];
    s[0] = c;
}

bool check_all_right_roll(char s1[7], char s2[7])
{
    int res = false;
    for (int i = 0; i < 4; i ++) {
        roll_right(s1);
        if (strcmp(s1, s2) == 0) res = true;
    }
    return res;
}

int main(void)
{
    char s[13], s1[7], s2[7];
    while (scanf("%s", s) != EOF) {
        strncpy(s1, s, 6);
        s1[6] = '\0';
        strncpy(s2, s+6, 7);

        bool res = false;
        for (int i = 0; i < 4; i ++) {
            roll_up(s1);
            res |= check_all_right_roll(s1, s2);
        }
        roll_right(s1);
        roll_up(s1);
        res |= check_all_right_roll(s1, s2);
        roll_up(s1);
        roll_up(s1);
        res |= check_all_right_roll(s1, s2);

        if (res) puts("TRUE");
        else puts("FALSE");
    }

    return 0;
}


习4-5 UVA 1590 互联网协议网络

思路

考察二进制与十进制之间的转换。

最小网络地址为最长公共前缀加上后面全部补零;子网掩码则为最长公共前缀对应的位全部置1,然后后面全部补零。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, c[32];

int main(void)
{
    while (cin >> n) {
        int x[4];
        memset(c, 0, sizeof(c));
        for (int k = 0; k < n; k ++) {
            scanf("%d.%d.%d.%d", &x[3], &x[2], &x[1], &x[0]);
            for (int i = 0; i < 4; i ++)
                for (int j = 0; j < 8; j ++)
                    c[i*8+j] += (x[i]&(1<<j)) ? 1 : 0;
        }
        int k = 32;
        for (k = 31; k >= 0; k --)
            if (c[k] != 0 && c[k] != n) break;

        memset(x, 0, sizeof(x));
        for (int i = 3; i >= 0; i --) {
            for (int j = 7; j >= 0; j --) {
                int k1 = i*8+j;
                if (k1 > k) x[i] |= ((c[k1]/n) << j);
            }
            printf("%d%c", x[i], (i == 0) ? '\n' : '.');
        }

        memset(x, 0, sizeof(x));
        for (int i = 3; i >= 0; i --) {
            for (int j = 7; j >= 0; j --) {
                int k1 = i*8+j;
                if (k1 > k) x[i] |= (1 << j);
            }
            printf("%d%c", x[i], (i == 0) ? '\n' : '.');
        }
    }

    return 0;
}


习4-6 UVA 508 Morse Mismatches

思路

首先这道题目紫书翻译错了。精确匹配的话应该输出第一个,而不是任意一个,如果有多个精确匹配,要加后缀“!”。未精确匹配的,不管是否有多个,都应该要加“?”。

在检查匹配时,可以转为计算两段morse编码的距离。首先两段编码的公共长度前缀必须相同,否则距离为无穷。然后距离为两段编码长度之差。

我的代码提交之后一直RE,查了很长时间才发现原因是:word数组定义成大小为100,而在while (cin >> word
&& word
[0] != ‘*’)这个地方n有可能是100,这样就超内存了。

代码

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

char morse[36][20];
int n;
string word[101];
string context[100];

int ctoi(char c)
{
    return isdigit(c) ? c-'0' : c-'A'+10;
}

void decode(string s)
{
    int resn = 0, resd = 80;
    string resc = "";
    int m = s.size();
    for (int i = 0; i < n; i++) {
        int k = context[i].size();
        int len = min(m, k);
        if (s.substr(0, len) == context[i].substr(0, len)) {
            int dis = abs(m-k);
            if (dis <= resd) {
                if (resd == 0)
                    resc = "!";
                else {
                    resn = i;
                    resd = dis;
                }
            }
        }
    }
    if (resd > 0) resc = "?";
    cout << word[resn] << resc << endl;
}

int main(void)
{
    char c[2];
    string s;
    while (cin >> c && c[0] != '*')
        cin >> morse[ctoi(c[0])];
    n = 0;
    while (cin >> word
 && word
[0] != '*') {
        for (int i = 0; word
[i]; i++)
            context
 += morse[ctoi(word
[i])];
        n++;
    }
    while (cin >> s && s[0] != '*')
        decode(s);

    return 0;
}


习4-7 UVA 509 RAID 技术

思路

RAID技术的原理认真读一下原题,不难理解。这个题考察的是异或运算以及多层循环的组织。变量定义和流程详见代码。

注意RAID数据保存出错的情况有两种:

1、某列有x的情况下,只能有一个x,否则无法恢复数据。

2、某列没有x的情况下,需要能通过校验。

代码

[code]#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;

const int D = 6;
const int S = 64;
const int B = 100;

int main()
{
    int kase = 0;
    int d, s, b, e;
    char data[D][S*B+1];
    while (scanf("%d", &d) && d) {
        char c[2];
        scanf("%d%d%s", &s, &b, c);
        e = (c[0] == 'E') ? 0 : 1;
        for (int i = 0; i < d; i++)
            scanf("%s", data[i]);

        bool flag = true;
        for (int i = 0; i < b; i++) {
            for (int j = i*s; j < (i+1)*s; j++) {
                int cntx = 0;
                int x = d;
                int num = 0;
                for (int k = 0; k < d; k++) {
                    if (data[k][j] == 'x') {
                        cntx++;
                        x = k;
                    } else
                        num ^= (data[k][j] - '0');
                }
                if (cntx > 1 || x == d && num != e) { flag = false; break;}
                if (x < d) data[x][j] = (num ^ e) + '0';
            }
            if (flag == false) break;
        }

        if (flag == false) {
            printf("Disk set %d is invalid.\n", ++kase);
            continue;
        }

        int n = 0;
        char res[(D-1)*S*B+4];
        for (int i = 0; i < b; i++) {
            int p = i % d;
            for (int k = 0; k < d; k++) {
                if (k == p) continue;
                strncpy(res+n, data[k]+i*s, s);
                n += s;
            }
        }
        while (n % 4) res[n++] = '0';
        res
 = '\0';

        printf("Disk set %d is valid, contents are: ", ++kase);
        for (int i = 0; i < n; i += 4) {
            int m = 0;
            for (int j = i; j < i+4; j++)
                m = m*2 + res[j] - '0';
            printf("%c", (m < 10) ? m+'0' : m-10+'A');
        }
        printf("\n");
    }

    return 0;
}


习4-8 UVA 12108 特别困的学生 (特困生)

思路

按照时间t一分一分往前走,每过一分钟检查一下学生的状态,每个学生如果到了睡眠-清醒状态变更的关键点时,检查是否满足变更条件。

实验证明如果t超过500时全班还没有达到清醒则不存在全班清醒的时刻。当然这很可能是因为该题的数据比较弱。。

代码

[code]#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 10;

struct Stu {
    int a, b, c;

    bool is_sleep()
    {
        return c > a;
    }

    int next_minute(int m, bool can_sleep)
    {
        if (++c > a+b) {c = 1; m--;}
        else if (c == a+1) {
            if (can_sleep) m++;
            else c = 1;
        }
        return m;
    }
};

int main()
{
    int n, m, t;
    Stu s
;
    int kase = 0;

    while (scanf("%d", &n) && n) {
        m = 0;
        for (int i = 0; i < n; i++) {
            scanf("%d%d%d", &s[i].a, &s[i].b, &s[i].c);
            if (s[i].is_sleep()) m++;
        }

        for (t = 1; t <= 500; t++) {
            if (m == 0) break;
            bool can_sleep = (m > n-m) ? 1 : 0;
            for (int i = 0; i < n; i++)
                m = s[i].next_minute(m, can_sleep);
        }

        if (m == 0) printf("Case %d: %d\n", ++kase, t);
        else printf("Case %d: -1\n", ++kase);
    }

    return 0;
}


习4-9 UVA 1591 数据挖掘

思路

认真的看了一遍却没看懂题意,看来的确如书中所说,对这个题兴趣不大。直接找了别人博客的分析参考着做的。

特别抽象的一道题,给出数据总量n,数组p的元素大小sp,数组q元素大小sq,求使Qofs’(i)=(Pofs(i)+Pofs(i) < < A)>>B成立的A、B的值,和q数组占用的空间k。

只需要从枚举32以内的A、B并判断是否合法即可,最终保留最小的k和使k最小的a、b。

另可参考博客code4101的博文

代码

[code]#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;

int main(){
    LL n, sp, sq;
    while (~scanf("%lld%lld%lld", &n, &sp, &sq)) {
        LL a, b, ansN = 1LL<<62, ansA = 0, ansB = 0;
        for (a = 0; a < 32; a++) {
            for(b = 0; b < 32; b++) {
              LL cur = (((n-1)*sp + ((n-1)*sp<<a)) >> b) + sq;
              if (cur >= n*sq && cur < ansN)
                ansN = cur, ansA = a, ansB = b;
            }
        }
        printf("%lld %lld %lld\n", ansN, ansA, ansB);
    }
    return 0;
}


习4-10 UVA 815 洪水!

思路

我看到这个题的第一想法就是二分法,这在紫书第八章才会介绍,但我在其它地方中已经学过了。具体是对水位的海拔高度做二分查找。

在做的过程中WA了好几次,确实有一些特殊情况没有考虑到,边界情况处理,负数的处理等。

另外其它方法还有很多,比如这两种:

1、把所有格子按海拔顺序排序,把每一档海拔与下一档之间能够容纳的水依次累加直到大于水的总体积。

2、把所有格子按海拔顺序排序,逐个计算含水海拔,第n个格子的含水海拔=(总水量+n个格子相对于0海拔的体积)/n个格子的面积和,顺序读入后面格子海拔,如果此格高于已算出的含水海拔则终止。

具体代码请参考其它博客。

代码

[code]#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 30;
const double EPS = 1e-6;

int k;
int h[N*N];
int sum;

bool check(double mid)
{
    double res = 0;
    for (int i = 0; i < k; i ++)
        res += (mid > h[i]) ? (mid - h[i]) : 0;
    return res*100 >= sum;
}

int cnt(double mid)
{
    int res = 0;
    for (int i = 0; i < k; i ++)
        res += (mid > h[i]) ? 1 : 0;
    return res;
}

int main(void)
{
    int t = 0;
    int m, n;
    while (cin >> m >> n, m || n) {
        k = m*n;
        for (int i = 0; i < k; i ++)
            scanf("%d", &h[i]);
        cin >> sum;

        double ub = 1<<30, lb = -(1<<30);
        while (ub - lb > EPS) {
            double mid = (ub + lb) / 2;
            if (check(mid)) ub = mid;
            else lb = mid;
        }

        printf("Region %d\n", ++t);
        printf("Water level is %.2lf meters.\n", ub);
        printf("%.2lf percent of the region is under water.\n\n",
                (double)(cnt(ub))*100/m/n);
    }

    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: