入门经典_Chap06_习题:搜索 数据结构 欧拉回路

前言

A题有一个坑:当输入字符串为空串的时候也需要处理,但是用cin读不进来空串,得改成getline
B题~D题是水题

UVA - 1600 -E - Patrol Robot

思路

    这题的意思是机器人要从一个m*n(1≤m,n≤20)网格的左上角(1,1)走到右下角(m,n)。网格中的一些格子是空地(用0表示),其他格子是障碍(用1表示)。
    机器人每次可以往4个方向走一格,但不能连续地穿越k(0≤k≤20)个障碍,求最短路长度。起点和终点保证是空地。

    bfs可以过,每一个结点维护还可以穿过多少个障碍。这样当扩展到非障碍物的时候,就将此属性重置为初始值,当扩展到障碍物的时候,就将此属性减1(前提是大于0)
    但是有一点需要注意,当扩展到障碍物时,不要将障碍物标记为已访问,因为障碍物可能会多次访问。

代码

int m, n, k, a[maxn][maxn];
bool vis[maxn][maxn];
struct node {
    int x, y, step, k;
    node(){}
    node(int i, int j, int s, int K):x(i), y(j), step(s), k(K){}
};

void init() { met(a, 0); met(vis, 0); }

int bfs() {
    queue<node> q; q.push(node(1,1,0,k)); vis[1][1] = 1;

    while(!q.empty()) {
        node t = q.front(); q.pop();
        if(t.x == n && t.y == m) return t.step;
        for(int i = 0; i < 4; ++i) {
            int tx = t.x + dir[i][0], ty = t.y + dir[i][1];
            if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && !vis[tx][ty]) {
                if(a[tx][ty] == 1) {
                    if(t.k > 0) q.push(node(tx, ty, t.step+1, t.k-1));
                }
                else {
                    vis[tx][ty] = 1; q.push(node(tx, ty, t.step+1, k));
                }
            }
        }
    }
    return -1;
}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    int t; cin >> t;
    while(t--) {
        init();
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j) {
            scanf("%d", &a[i][j]);
        }
        if(n == 1 && m == 1) { printf("1\n"); continue; }
        printf("%d\n", bfs());
    }

    return 0;
}

UVA - 12166 -F - Equilibrium Mobile

思路

    给一个深度不超过16的二叉树,代表一个天平。每根杆都悬挂在中间,每个秤砣的重量已知。问至少修改多少个秤砣的重量才能让天平平衡。
例子

    将每一个结点上升到最高层,在上升的过程中,每上升一层,就将其值乘以2,当它上升到最高层的时候表示最高层结点为此数值的时候此结点不需要更改。
    这样我只要统计出都上升到最高层之后出现的次数最多的那个数,将它作为不用更改的数,而剩下的数的个数即为答案。

    比如上面的图,三个结点都上升到最高层后依次是12, 28, 12, 所以12 出现的次数最多,说明最高层为12的时候不需要修改的结点最多(3和6),那么剩下的7就是要修改的了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("in.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> Pii;
typedef map<string, int> Msi;
typedef map<LL, int> Mli;
typedef set<int> Si;
typedef vector<int> Vi;
const int maxn = 1e6 + 100;
const int INF = 0x7fffffff;
const double eps = 1e-2;
int dir[4][2] = { 1,0, -1,0, 0,1, 0,-1 };

int a;
LL tot, cur;
char c;
string s;
Mli m;

void build(int deep) {
    if(s[cur] == '[') {
        ++cur;
        build(deep+1); ++cur; ++cur; build(deep+1); ++cur;
    }
    else if(s[cur] >= '0' || s[cur] <= '9'){
        LL x = 0;
        while(cur < s.length() && s[cur] >= '0' && s[cur] <= '9') x = x*10+s[cur]-'0', ++cur;
        --cur;
        LL tmp = 0LL + x*(1<<deep);
        if(!m.count(tmp)) m[tmp] = 0;
        ++m[tmp]; ++tot;
    }
    else ++cur;

}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    int t; cin >> t; getchar();
    while(t--) {
        cin >> s;
        m.clear(); tot = 0; cur = 0;
        build(0);
        LL ans = 0;
        for(Mli::iterator it = m.begin(); it != m.end(); ++it) {
            ans = max(ans, 0LL+it->second);
        }
        cout << tot-ans <<endl;
    }

    return 0;
}

UVA - 804 -G - Petri Net Simulation

思路

    模拟题,理解意思之后应该能写的出来。
    如果不循环的话很容易模拟,个人认为本题的难点是如何判断循环的产生,我是统计了总的输入和输出,当总输入>总输出的时候一定不会有循环产生,因为每一步都是减少的嘛,最后肯定会减到0,否则的话,我便将初始时每一个p里的个数记录下来,如果某个过程中出现了和初始状态一样的情况,那么循环节就找到了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("in.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> Pii;
typedef map<string, int> Msi;
typedef map<int, int> Mii;
typedef set<int> Si;
typedef vector<int> Vi;
const int maxn = 1e6 + 100;
const int INF = 0x7fffffff;
const double eps = 1e-2;
int dir[4][2] = { 1,0, -1,0, 0,1, 0,-1 };

int n, m, p[maxn], pre[maxn], f, a, numI[maxn], numO[maxn];
Vi in[maxn], ot[maxn];

void init() {
    for(int i = 0; i <= m; ++i) in[i].clear(), ot[i].clear(), numI[i] = numO[i] = 0;
}

bool check(int x) {
    Mii m;
    for(int i = 0; i < in[x].size(); ++i) {
        if(!m.count(in[x][i])) m[in[x][i]] = 0; ++m[in[x][i]];
    }
    for(Mii::iterator it = m.begin(); it != m.end(); ++it) {
        if(p[it->first] < it->second) return 0;
    }
    return 1;
}

void moves(int x) {
    for(int i = 0; i < in[x].size(); ++i) --p[in[x][i]];
    for(int i = 0; i < ot[x].size(); ++i) ++p[ot[x][i]];
}

bool isPre() {
    for(int i =1; i <= n; ++i) if(p[i] != pre[i]) return 0;
    return 1;
}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    int kase = 0;
    while(scanf("%d", &n) == 1 && n) {
        init();
        for(int i = 1; i <= n; ++i) scanf("%d", &p[i]);
        memcpy(pre, p, sizeof(p));
        scanf("%d", &m);
        for(int i = 1; i <= m; ++i) {
            while(scanf("%d", &a) == 1 && a) {
                //cout << a << " ";
                if(a < 0) in[i].push_back(-a), ++numI[i];
                else ot[i].push_back(a), ++numO[i];
            }
            //cout << endl;
        }
        scanf("%d", &f);

        int sum = 0;
        for(int i = 1; i <= m; ++i) {
            sum += numI[i]; sum -= numO[i];
        }

        int ans = 1, ret = 1; bool Cir = 0;
        for(int k = 0; k < f; ++k) {
            bool ok = 0;
            for(int i = 1; i <= m; ++i) {
                if(check(i)) { moves(i), ok = 1; break; }
            }
            if(!ok) {ans = 0; ret = k; break; }
            if(sum <= 0) if(isPre()) { Cir = 1; ret = k+1; break; }
        }

        if(Cir) {
            int t = (f-1)%ret+1;
            for(int k = 1; k <= t; ++k) {
                bool ok = 0;
                for(int i = 1; i <= m; ++i) {
                    if(check(i)) { moves(i), ok = 1; break; }
                }
                if(!ok) { ans = 0; break; }
            }
        }

        if(ans) printf("Case %d: still live after %d transitions\n", ++kase, f);
        else printf("Case %d: dead after %d transitions\n", ++kase, ret);

        printf("Places with tokens:");
        for(int i = 1; i <= n; ++i) {
            if(p[i] > 0) printf(" %d (%d)", i, p[i]);
        }
        printf("\n\n");
    }

    return 0;
}

UVA - 806 -H - Spatial Structures

思路

    四分树水题,递归不解释。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e3 + 100;
const int INF = 0x7fffffff;
typedef set<int> Set;
typedef vector<int> Vi;

int n, t, b[maxn*maxn], bid, ans[maxn*maxn], aid;
char a[maxn][maxn], res[maxn][maxn];
string s;

void genAns(string str) {
    int len = str.length(); ans[aid] = 0;
    for(int i = len-1; i >= 0; --i) {
        ans[aid] = ans[aid]*5 + (str[i]-'0');
    }
    //cout << ans[aid] <<endl;
    ++aid;
}

int check(int x, int y, int L) {
    bool f1 = 0, f2 = 0;
    for(int i = 0; i < L; ++i)
    for(int j = 0; j < L; ++j) {
        if(a[x+i][y+j] == '0') f1 = 1;
        else f2 = 1;
    }
    if(f1 && f2) return -1;
    if(f1 && !f2) return 0;
    return 1;
}
void dfs(int x, int y, int L, string str) {
    int ck = check(x, y, L);
    if(ck == 1) { genAns(str); return; }
    if(ck == 0) return;
    dfs(x,      y,      L/2, str+'1');
    dfs(x,      y+L/2,  L/2, str+'2');
    dfs(x+L/2,  y,      L/2, str+'3');
    dfs(x+L/2,  y+L/2,  L/2, str+'4');

}

void genImg(int x) {
    string s = "";
    while(x) {
        s += x%5+'0';
        x /= 5;
    }
    //cout << s <<endl;
    int len = s.length(), cx = 1, cy = 1, L = n;
    for(int i = 0; i < len; ++i) {
        L /= 2;
        if(s[i] == '1'){
        }
        else if(s[i] == '2') {
            cy += L;
        }
        else if(s[i] == '3') {
            cx += L;
        }
        else {
            cx += L; cy += L;
        }
    }
    for(int i = 0; i < L; ++i)
    for(int j = 0; j < L; ++j) {
        res[cx+i][cy+j] = '*';
    }
}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    int kase = 0, line = 0;
    while(scanf("%d", &n) == 1 && n) {
        if(line) printf("\n"); line = 1;
        if(n < 0) {
            n = -n; bid = 0; met(res, '.');
            while(scanf("%d", &t) == 1 && t != -1) b[bid++] = t;
            for(int i = 0; i < bid; ++i) {
                genImg(b[i]);
            }

            printf("Image %d\n", ++kase);
            for(int i = 1; i <= n; ++i){
                for(int j = 1; j <= n; ++j) printf("%c", res[i][j]);printf("\n");
            }

            //printf("\n");
        }
        else {
            for(int i = 1; i <= n; ++i) scanf("%s", a[i]+1);
            s = ""; aid = 0;
            dfs(1,1,n,s);
            sort(ans, ans+aid);

            printf("Image %d\n", ++kase);
            if(aid> 0) {
                printf("%d", ans[0]);
                for(int i = 1; i < aid; ++i) {
                    if(i%12 == 0) printf("\n");
                    else printf(" ");
                    printf("%d", ans[i]);
                }
                printf("\n");
            }
            printf("Total number of black nodes = %d\n", aid);
        }

    }

    return 0;
}

UVA - 127 -I - “Accordian” Patience

思路

    题目意思是把52张牌从左到右排好,每张牌自成一个牌堆(pile)。当某张牌与它左边那张牌或者左边第3张牌“match”(花色suit或者点数rank相同)时,就把这张牌移到那张牌上面。
    移动之后还要查看是否可以进行其他移动。只有位于牌堆顶部的牌才能移动或者参与match。当牌堆之间出现空隙时要立刻把右边的所有牌堆左移一格来填补空隙。
    如果有多张牌可以移动,先移动最左边的那张牌;如果既可以移一格也可以移3格时,移3格。按顺序输入52张牌,输出最后的牌堆数以及各牌堆的牌数。

    双向链表模拟一下就可以了。注意1的时候pile不加s,因为这个wa了三遍-_-||

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a, b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("out.txt", "w", stdout);
#define DT freopen("in.txt", "a", stdout);
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 5;
const int INF = (1 << 31) - 1;
int dir[4][2] = {1,0,-1,0,0,1,0,-1};

char st[] = "CDHS";
char rk[] = "A23456789TJQK";
int getSuit(char c) { return strchr(st, c) - st+1; }
int getRank(char c) { return strchr(rk, c) - rk+1; }

char s[60][5];
int Left[maxn], Right[maxn], Next[maxn], ans[maxn];

void init() {
    met(ans, 0);
    for(int i = 0; i <= 52; ++i) Next[i] = -1;
}
void Link(int x, int y) {
    Right[x] = y; Left[y] = x;
}

int getN(char s[]) {
    return (getRank(s[0])-1)*4+getSuit(s[1]);
}

int get1(int x) { return Left[x]; }
int get3(int x) {
    int cur = x;
    for(int i = 0; i < 3; ++i) {
        if(cur == 0) return 0;
        cur = Left[cur];
    }
    return cur;
}

bool check(int cur, int pre) {
    return cur%4 == pre%4 || (cur-1)/4+1 == (pre-1)/4+1;
}

void change(int cur, int pre) {
    int LP = Left[pre], RP = Right[pre];
    int LC = Left[cur], RC = Right[cur];
    int NC = Next[cur];
    Next[cur] = pre;
    Link(LP, cur); Link(cur, RP);
    if(NC == -1) Link(LC, RC);
    else { Link(LC, NC); Link(NC, RC); }
}

void change2(int cur, int pre) {
    int LP = Left[pre];
    int RC = Right[cur];
    int NC = Next[cur];
    Next[cur] = pre;
    Link(LP, cur);
    if(NC != -1) { Link(cur, NC); Link(NC, RC); }
}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    while(scanf("%s", s[1]) == 1 && s[1][0] != '#') {
        init();
        for(int i = 2; i <= 52; ++i) scanf("%s", s[i]);
        for(int i = 1; i <= 52; ++i) {
            //int u = getRank(s[i][0]),   v = getSuit(s[i][1]);
            //int up= getRank(s[i+1][0]), vp= getSuit(s[i+1][1]);
            if(i == 1) Link(0,getN(s[i]));
            if(i == 52) { Link(getN(s[i]), 0); continue; }
            Link(getN(s[i]), getN(s[i+1]));
        }

        //cout << get3(getN("5S")) << endl;
        int cur = Right[0];
        while(cur != 0) {
            int pre = get3(cur);
            if(pre != 0 && check(cur, pre)) {
                change(cur, pre);
            }
            else {
                int p = get1(cur);
                if(p != 0 && check(cur, p)) {
                    change2(cur, p);
                }
                else cur = Right[cur];
            }
        }

        int aid = 0;
        for(int i = Right[0]; i != 0; i = Right[i]) {
            int c = i;
            while(c != -1) {
                ++ans[aid]; c = Next[c];
            }
            ++aid;
        }
        if(aid > 1) printf("%d piles remaining:", aid);
        else printf("%d pile remaining:", aid);
        for(int i = 0; i < aid; ++i) printf(" %d", ans[i]); printf("\n");

    }

    return 0;
}

UVA - 246 -J - 10-20-30

待写

UVA - 10410 -K - Tree Reconstruction

思路

    very important and good 的一道题!
    输入一个n(n≤1000)结点树的BFS序列和DFS序列,输出每个结点的子结点列表。输入序列(不管是BFS还是DFS)是这样生成的:当一个结点被扩展时,其所有子结点应该按照编号从小到大的顺序访问。

    先给一个某大神写的题解链接点此跳转

    这一题的难点个人认为是当两个结点(设为uv)在dfs和bfs都是挨着的时候,就涉及到一个问题,v即可能是u的最左孩子,也可能是u的右兄弟。但是反过来想,把右兄弟换到左孩子的位置不行,会改变bfs的序列,但是如果将最左孩子换到右兄弟的位置,bfs和dfs的序列还是不变的,那就都当右兄弟处理好了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a, b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("out.txt", "w", stdout);
#define DT freopen("in.txt", "a", stdout);
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 5;
const int INF = (1 << 31) - 1;
int dir[4][2] = {1,0,-1,0,0,1,0,-1};

int n, a[maxn], b[maxn], id[maxn];
vector<int> v[maxn];

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    while(scanf("%d", &n) == 1) {
        for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), id[a[i]] = i, v[i].clear();
        for(int i = 1; i <= n; ++i) scanf("%d", &b[i]);

        stack<int> s; s.push(b[1]);
        for(int i = 2; i <= n; ++i) {
            while(!s.empty()) {
                int t = s.top();
                if(id[b[i]] > id[t] + 1 || t == b[1]) {
                    v[t].push_back(b[i]);
                    s.push(b[i]); break;
                }
                else s.pop();
            }
        }

        for(int i = 1; i <= n; ++i) {
            printf("%d:", i);
            for(int j = 0; j < v[i].size(); ++j) printf(" %d", v[i][j]); printf("\n");
        }

    }

    return 0;
}

UVA - 810 -L - A Dicey Problem

待写

UVA - 215 -M - Spreadsheet Calculator

待写

UVA - 12118 -N - Inspector’s Dilemma

In a country, there are a number of cities. Each pair of city is connected by a highway, bi-directional of course. A road-inspector’s task is to travel through the highways (in either direction) and to check if everything is in order. Now, a road-inspector has a list of highways he must inspect. However, it might not be possible for him to travel through all the highways on his list without using other highways. He needs a constant amount of time to traverse any single highway. As you can understand, the inspector is a busy fellow does not want to waste his precious time. He needs to know the minimum possible time to complete his task. He has the liberty to start from and end with any city he likes. Please help him out.

Input

The input file has several test cases. First line of each case has three integers: V (1 ≤ V ≤ 1000), the number of cities, E (0 ≤ E ≤ V ∗ (V − 1)/2), the number of highways the inspector needs to check and T (1 ≤ T ≤ 10), time needed to pass a single highway. Each of the next E lines contains two integers a and b (1 ≤ a, b ≤ V , a ̸= b) meaning the inspector has to check the highway between cities a and b.
The input is terminated by a case with V = E = T = 0. This case should not be processed.

Output

For each test case, print the serial of output followed by the minimum possible time the inspector needs to inspect all the highways on his list. Look at the output for sample input for details.

Sample Input

5 3 1
1 2
1 3
4 5
4 4 1
1 2
1 4
2 3
3 4
0 0 0

Sample Output

Case 1: 4
Case 2: 4

思路

  题目意思是某国家有V(V≤1000)个城市,每两个城市之间都有一条双向道路直接相连,长度为T。请你找一条最短的道路(起点和终点任意),使得该道路经过E条指定的边。

  找出所有的连通块(bfs,dfs都行),对每一个连通块,找出其内度为奇数的顶点的个数(如果此个数小于2则使其为2,此时即为拿出一个偶数点向外引两条边).
  将所有连通块返回的奇数点个数相加得到一个数ans, ans/2就是可以将所有连通块连接起来而且使最后组成的大连通块为欧拉回路(没有奇数顶点)的最小边数,但是由于最后组成的图形不一定要是欧拉回路,是欧拉道路即可,所以还可以减去一个边,即最后要加的边数为ans/2-1
  还有一点要注意的是,如果我本来就不用加任何边,但是我最后却减了一个,这样ans就小于0了,所以ans最后要和0调和一下。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a, b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("out.txt", "w", stdout);
#define DT freopen("in.txt", "a", stdout);
using namespace std;
typedef long long LL;
const int maxn = 1e3 + 100;
const int INF = (1 << 31) - 1;
int dir[4][2] = {1,0,-1,0,0,1,0,-1};

int v, e, t, a, b, in[maxn], maps[maxn][maxn], kase = 0;
bool vis[maxn];
void init() { met(in, 0); met(maps, 0); met(vis, 0); }

int bfs(int x) {
    int cnt = 0; queue<int> q;
    q.push(x); vis[x] = 1;

    while(!q.empty()) {
        int t = q.front(); q.pop();
        if(in[t]&1) ++cnt;
        for(int i = 1; i <= v; ++i) {
            if(!vis[i] && maps[t][i] == 1) {
                vis[i] = 1;
                q.push(i);
            }
        }
    }
    return max(cnt, 2);
}

int main() {
    #ifdef _LOCAL
    IN;
    #endif // _LOCAL

    while(scanf("%d%d%d", &v, &e, &t) == 3) {
        if(v == 0 && e == 0 && t == 0) continue;
        init();
        for(int i = 0; i < e; ++i) {
            scanf("%d%d", &a, &b);
            maps[a][b] = maps[b][a] = 1;
            ++in[a], ++in[b];
        }

        int ans = 0;
        for(int i = 1; i <= v; ++i) {
            if(!vis[i] && in[i]) {
                ans += bfs(i);
            }
        }
        ans = (ans-2)/2; ans = max(ans, 0);
        printf("Case %d: %d\n", ++kase, (e+ans)*t);
    }


    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值