基础搜索算法题解(N-R)

来源:互联网 发布:动感单车品牌 知乎 编辑:程序博客网 时间:2024/06/05 03:23

练习链接:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=171#overview


N题 Shredding Company

Time Limit: 1000MS
Memory Limit: 10000KTotal Submissions: 4398
Accepted: 2520

Description

You have just been put in charge of developing a new shredder for the Shredding Company Although a "normal" shredder would just shred sheets of paper into little pieces so that the contents would become unreadable, this new shredder needs to have the following unusual basic characteristics. 

1.The shredder takes as input a target number and a sheet of paper with a number written on it. 
2.It shreds (or cuts) the sheet into pieces each of which has one or more digits on it. 
3.The sum of the numbers written on each piece is the closest possible number to the target number, without going over it. 

For example, suppose that the target number is 50, and the sheet of paper has the number 12346. The shredder would cut the sheet into four pieces, where one piece has 1, another has 2, the third has 34, and the fourth has 6. This is because their sum 43 (= 1 + 2 + 34 + 6) is closest to the target number 50 of all possible combinations without going over 50. For example, a combination where the pieces are 1, 23, 4, and 6 is not valid, because the sum of this combination 34 (= 1 + 23 + 4 + 6) is less than the above combination's 43. The combination of 12, 34, and 6 is not valid either, because the sum 52 (= 12 + 34 + 6) is greater than the target number of 50. 
 
Figure 1. Shredding a sheet of paper having the number 12346 when the target number is 50


There are also three special rules : 
1.If the target number is the same as the number on the sheet of paper, then the paper is not cut. 
For example, if the target number is 100 and the number on the sheet of paper is also 100, then 
the paper is not cut. 

2.If it is not possible to make any combination whose sum is less than or equal to the target number, then error is printed on a display. For example, if the target number is 1 and the number on the sheet of paper is 123, it is not possible to make any valid combination, as the combination with the smallest possible sum is 1, 2, 3. The sum for this combination is 6, which is greater than the target number, and thus error is printed. 

3.If there is more than one possible combination where the sum is closest to the target number without going over it, then rejected is printed on a display. For example, if the target number is 15, and the number on the sheet of paper is 111, then there are two possible combinations with the highest possible sum of 12: (a) 1 and 11 and (b) 11 and 1; thus rejected is printed. In order to develop such a shredder, you have decided to first make a simple program that would simulate the above characteristics and rules. Given two numbers, where the first is the target number and the second is the number on the sheet of paper to be shredded, you need to figure out how the shredder should "cut up" the second number. 

Input

The input consists of several test cases, each on one line, as follows : 
tl num1 
t2 num2 
... 
tn numn 
0 0 
Each test case consists of the following two positive integers, which are separated by one space : (1) the first integer (ti above) is the target number, (2) the second integer (numi above) is the number that is on the paper to be shredded. 
Neither integers may have a 0 as the first digit, e.g., 123 is allowed but 0123 is not. You may assume that both integers are at most 6 digits in length. A line consisting of two zeros signals the end of the input. 

Output

For each test case in the input, the corresponding output takes one of the following three types : 
sum part1 part2 ... 
rejected 
error 

In the first type, partj and sum have the following meaning : 
1.Each partj is a number on one piece of shredded paper. The order of partj corresponds to the order of the original digits on the sheet of paper. 
2.sum is the sum of the numbers after being shredded, i.e., sum = part1 + part2 +... 
Each number should be separated by one space. 
The message error is printed if it is not possible to make any combination, and rejected if there is 
more than one possible combination. 
No extra characters including spaces are allowed at the beginning of each line, nor at the end of each line. 

Sample Input

50 12346376 144139927438 92743818 33129 314225 1299111 33333103 8621506 11040 0

Sample Output

43 1 2 34 6283 144 139927438 92743818 3 3 12error21 1 2 9 9rejected103 86 2 15 0rejected

Source

Japan 2002 Kanazawa

题目链接:http://poj.org/problem?id=1416


题目大意:输入两个数n和t,将数字t分解求和,求所得到的小于等于n并最接近n的解,并输出分解方案,如果不存在这样的分解输出error,如果有多组解输出rejected

分析第一组样例:50  12346,其最优分解为1 2 34 6,和为43
第七组样例:111 33333,其最优分解为33 33 3或者3 33 33不唯一,故输出rejected

题目分析:易得最小分解和为把每位数字都拆分下来求和,若这时候和依旧大于n则无解,若n与t相等,则直接输出n
对于其他情况,数字要用字符串存,因为要考虑前导0,题目说的Neither integers may have a 0 as the first digit, e.g., 123 is allowed but 0123 is not. 是针对输入而言的,搜索时我们采用类似两点法的思想,DFS中有三个参数sum表示当前的和,st和ed分别表示当前分解位置的头和尾,每次搜索时我们通过st和ed来得到当前分解出来的数字cur,对于要输出的解我们可以通过对下标位的标记来实现,即若从某位开始要拆则该位标记为true,
如果不拆,则DFS(sum,st,ed+1)表示当前和不变,ed向后扩一位,
如果拆,    则DFS(sum+cur,ed,ed+1)表示当前位拆分,和加上cur,下一次搜索的头位置为当前的尾位置
当ed=len时,一次分解策略结束
若比之前的和大,则更新ans,跟新ans时rejected要设置为false,因为才更新的时候最优的ans只有一个,即为刚更新的值
若与之前相同。则rejected标记为true,表示当前最优解不止一种分解法

#include <cstdio>#include <cstring>int n, len, ans;char s[10];bool rejected;bool mark[10], tmark[10];//当前的和,当前数字的头下标,当前数字的尾下标void DFS(int sum, int st, int ed){    if(sum > n)        return;    int cur = 0;    for(int i = st; i < ed; i++)    {        cur *= 10;        cur += (s[i] - '0');    }    if(ed == len)    {        sum += cur;        if(sum <= n && ans >= 0)        {            if(ans < sum)            {                ans = sum;                rejected = false;                for(int i = 0; i < len; i++)                    mark[i] = tmark[i];            }            else if(sum == ans)                rejected = true;        }        return;    }    tmark[ed] = false;    DFS(sum, st, ed + 1); //不拆    tmark[ed] = true;    DFS(sum + cur, ed, ed + 1) ;//拆}int main(){    int t;    while(scanf("%d %d", &n, &t) && (n + t))    {        rejected = false;        int tmp = t, sum = 0;        while(tmp)        {            sum += (tmp % 10);            tmp /= 10;        }        if(sum > n)        {            printf("error\n");            continue;        }        if(n == t)        {            printf("%d %d\n", n, n);            continue;        }        sprintf(s, "%d", t);            len = strlen(s);        ans = 0;        tmark[0] = true;        DFS(0, 0, 1);        if(rejected)            printf("rejected\n");        else if(ans > 0)        {            printf("%d", ans);            for(int i = 0; i < len; i++)            {                if(mark[i])                    printf(" ");                printf("%c", s[i]);            }            printf("\n");        }    }}




O题 Sudoku
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 14368 Accepted: 7102 Special Judge

Description

Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3x3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3x3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task. 

Input

The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0.

Output

For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them.

Sample Input

1103000509002109400000704000300502006060000050700803004000401000009205800804000107

Sample Output

143628579572139468986754231391542786468917352725863914237481695619275843854396127

Source

Southeastern Europe 2005

题目链接:http://poj.org/problem?id=2676

题目大意:就是数独咯,让你求解数独游戏,9乘9的矩阵要求每行每列和9个3*3的子矩阵内都出现数字1-9

题目分析:9乘9的矩阵,从第一个位置一直搜到最后一个位置,若当前位置有数字搜下一位置,否则枚举1-9并判断,
判断时当前行r = n/9当前列为c = n%9当前子矩阵的第一个元素位置为r / 3 * 3,c / 3 * 3

#include <cstdio>char s[10];int num[9][9];bool flag;bool ok(int n, int cur) {    int r = n / 9;//当前行int c = n % 9;//当前列    for(int j = 0; j < 9; j++)  //枚举列        if (num[r][j] == cur)             return false;    for(int i = 0; i < 9; i++)  //枚举行        if (num[i][c] == cur)             return false;//得到当前所在的子矩阵的第一个元素位置    int x = r / 3 * 3;    int y = c / 3 * 3;//枚举子矩阵中的元素    for(int i = x; i < x + 3; i++)        for(int j = y; j < y + 3; j++)            if (num[i][j] == cur)                 return false;    return true;}void DFS(int n){    if(n > 80 || flag)     {        flag = true;        return;    }if(num[n / 9][n % 9])//当前位置有数字直接搜索下一位{        DFS(n + 1);if(flag)return;}    else    {        for(int cur = 1; cur <= 9; cur++)  //枚举数字        {            if(ok(n, cur)) //若ok则插入            {                num[n / 9][n % 9] = cur;                 DFS(n + 1);                if(flag)                     return;                num[n / 9][n % 9] = 0; //还原            }        }    }}int main(){       int T;    scanf("%d", &T);    while(T--)    {        flag = false;        for(int i = 0; i < 9; i++) //得到数独矩阵{scanf("%s", s);            for(int j = 0; j < 9; j++)                num[i][j] = (s[j] - '0');}        DFS(0);  //从第一位开始搜        for(int i = 0; i < 9; i++)        {            for(int j = 0; j < 9; j++)                printf("%d", num[i][j]);            printf("\n");              }    }}




                                                         P题 Channel Allocation
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 12678 Accepted: 6498

Description

When a radio station is broadcasting over a very large area, repeaters are used to retransmit the signal so that every receiver has a strong signal. However, the channels used by each repeater must be carefully chosen so that nearby repeaters do not interfere with one another. This condition is satisfied if adjacent repeaters use different channels. 

Since the radio frequency spectrum is a precious resource, the number of channels required by a given network of repeaters should be minimised. You have to write a program that reads in a description of a repeater network and determines the minimum number of channels required.

Input

The input consists of a number of maps of repeater networks. Each map begins with a line containing the number of repeaters. This is between 1 and 26, and the repeaters are referred to by consecutive upper-case letters of the alphabet starting with A. For example, ten repeaters would have the names A,B,C,...,I and J. A network with zero repeaters indicates the end of input. 

Following the number of repeaters is a list of adjacency relationships. Each line has the form: 

A:BCDH 

which indicates that the repeaters B, C, D and H are adjacent to the repeater A. The first line describes those adjacent to repeater A, the second those adjacent to B, and so on for all of the repeaters. If a repeater is not adjacent to any other, its line has the form 

A: 

The repeaters are listed in alphabetical order. 

Note that the adjacency is a symmetric relationship; if A is adjacent to B, then B is necessarily adjacent to A. Also, since the repeaters lie in a plane, the graph formed by connecting adjacent repeaters does not have any line segments that cross. 

Output

For each map (except the final one with no repeaters), print a line containing the minumum number of channels needed so that no adjacent channels interfere. The sample output shows the format of this line. Take care that channels is in the singular form when only one channel is required.

Sample Input

2A:B:4A:BCB:ACDC:ABDD:BC4A:BCDB:ACDC:ABDD:ABC0

Sample Output

1 channel needed.3 channels needed.4 channels needed. 

Source

Southern African 2001

题目链接:http://poj.org/problem?id=1129

题目大意:有n组关系,每组关系用字母表示,每个字母要被赋一个值,关系A:BC要求A的值与BC都不同,问满足这n组关系最少需要赋上多少个不同的值

题目分析:因为题目说了,如果A与B值不同则必满足B与A值不同(其实就是说输入保证合法)则可以构建一个无向平面图,运用四色定理,DFS枚举出答案。

#include<cstdio>#include<cstring>int map[30][30];int color[30];int n,col;bool flag;char s[30];bool ok(int i){//若与i直接相连的各点值均与i点值不同则返回true    for(int j = 0; j < 26; j++){if(!map[i][j])continue;        if(color[i] == color[j])return false;}    return true;}void dfs(int num){    if(num == n) //若num与n相等,则着色完毕    {        flag = true;        return;    }    for(int i = 1; i <= col; i++) //枚举col个数字用来标记    {        color[num] = i;//赋值        if(ok(num))//若当前赋值符合条件搜索下一个dfs(num + 1);        color[num] = 0;//还原    }}int main(){    while(scanf("%d", &n) && n)    {flag = false;        memset(map, 0, sizeof(map));        for(int i = 0; i < n; i++) //构图        {            scanf("%s", s);int len = strlen(s);            for(int j = 2; j < len; j++)                map[i][s[j]-'A'] = map[s[j]-'A'][i] = 1;        }         for(col = 1; col <= 4; col++) //根据四色定理枚举1-4,代表用几个数字可以标记完        {            dfs(0); //从第一个字母开始搜索赋值            if(flag)break;        }        if(col == 1) printf("1 channel needed.\n");        else printf("%d channels needed.\n", col);    }    return 0;}




Q题 图的深度优先遍历序列

时间限制(普通/Java) :1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 1083            测试通过 : 327

题目描述

图(graph)是数据结构 G=(V,E),其中V是G中结点的有限非空集合,结点的偶对称为边(edge);E是G中边的有限集合。设V={0,1,2,……,n-1},图中的结点又称为顶点(vertex),有向图(directed graph)指图中代表边的偶对是有序的,用<u,v>代表一条有向边(又称为弧),则u称为该边的始点(尾),v称为边的终点(头)。无向图(undirected graph)指图中代表边的偶对是无序的,在无向图中边(u,v )和(v,u)是同一条边。

输入边构成无向图,求以顶点0为起点的深度优先遍历序列。



输入

第一行为两个整数ne,表示图顶点数和边数。以下e行每行两个整数,表示一条边的起点、终点,保证不重复、不失败。1n20,0e190

输出

前面n行输出无向图的邻接矩阵,最后一行输出以顶点0为起点的深度优先遍历序列,对于任一起点,首先遍历的是终点序号最小的、尚未被访问的一条边。每个序号后输出一个空格。

样例输入

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

样例输出

0 1 0 1 
1 0 1 1 
0 1 0 1 
1 1 1 0 
0 1 2 3 


题目链接:http://202.119.236.66/acmhome/problemdetail.do?&method=showdetail&id=1047

这题就用来宣传一下noj~

#include <cstdio> #include <cstring> int map[25][25]; bool vis[25]; int n, e; void DFS(int a) {     vis[a] = true;     printf("%d ", a);     for(int i = 0; i < n; i++)         if(map[a][i] && !vis[i])             DFS(i); } int main() {     int x, y;     scanf("%d %d", &n, &e);     memset(map, 0, sizeof(map));     memset(vis, false, sizeof(vis));     for(int i = 0; i < e; i++)     {         scanf("%d %d", &x, &y);         map[x][y] = 1;         map[y][x] = 1;     }     for(int i = 0; i < n; i++)     {         for(int j = 0; j < n; j++)             printf("%d ", map[i][j]);         printf("\n");     }     for(int i = 0; i < n; i++)         if(!vis[i])             DFS(i);     printf("\n"); }






R题  Stealing Harry Potter's Precious

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1982    Accepted Submission(s): 931
Problem Description
  Harry Potter has some precious. For example, his invisible robe, his wand and his owl. When Hogwarts school is in holiday, Harry Potter has to go back to uncle Vernon's home. But he can't bring his precious with him. As you know, uncle Vernon never allows such magic things in his house. So Harry has to deposit his precious in the Gringotts Wizarding Bank which is owned by some goblins. The bank can be considered as a N × M grid consisting of N × M rooms. Each room has a coordinate. The coordinates of the upper-left room is (1,1) , the down-right room is (N,M) and the room below the upper-left room is (2,1)..... A 3×4 bank grid is shown below:



  Some rooms are indestructible and some rooms are vulnerable. Goblins always care more about their own safety than their customers' properties, so they live in the indestructible rooms and put customers' properties in vulnerable rooms. Harry Potter's precious are also put in some vulnerable rooms. Dudely wants to steal Harry's things this holiday. He gets the most advanced drilling machine from his father, uncle Vernon, and drills into the bank. But he can only pass though the vulnerable rooms. He can't access the indestructible rooms. He starts from a certain vulnerable room, and then moves in four directions: north, east, south and west. Dudely knows where Harry's precious are. He wants to collect all Harry's precious by as less steps as possible. Moving from one room to another adjacent room is called a 'step'. Dudely doesn't want to get out of the bank before he collects all Harry's things. Dudely is stupid.He pay you $1,000,000 to figure out at least how many steps he must take to get all Harry's precious.
 

Input
  There are several test cases.
  In each test cases:
  The first line are two integers N and M, meaning that the bank is a N × M grid(0<N,M <= 100).
  Then a N×M matrix follows. Each element is a letter standing for a room. '#' means a indestructible room, '.' means a vulnerable room, and the only '@' means the vulnerable room from which Dudely starts to move.
  The next line is an integer K ( 0 < K <= 4), indicating there are K Harry Potter's precious in the bank.
  In next K lines, each line describes the position of a Harry Potter's precious by two integers X and Y, meaning that there is a precious in room (X,Y).
  The input ends with N = 0 and M = 0
 

Output
  For each test case, print the minimum number of steps Dudely must take. If Dudely can't get all Harry's things, print -1.
 

Sample Input
2 3##@#.#12 24 4#@##....####....22 12 40 0
 

Sample Output
-15
 

Source
2013 Asia Hangzhou Regional Contest
 
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4771

题目大意:一个人要在一个n*m的地图中找k个宝藏,求其收集完所有宝藏走过的最短路

题目分析:因为宝藏数不大于4,我们可以求出起点和所有宝藏两两之间的最短路,用BFS搜所出来,然后再枚举所有情况,找最短的那条即可,排列数stl中有个叫next_permutation()的函数相当好用啊
比如样例二起点是(1,2)宝藏在(2,1)和(2,3)那我们把起点设为A点,两个宝藏设为B,C,就两种可能ABC和ACB,再通过求出来的两点间的最短路得到这两种情况的总路程,最后取小的那个即为答案,代码狂丑....

#include <cstdio>#include <cstring>#include <queue>#include <string>#include <algorithm>#include <iostream>using namespace std;int const MAX = 100 + 5;int map[MAX][MAX];int n, m, k, cnt;int dx[] = {1, 0, -1, 0};int dy[] = {0, 1, 0, -1};char s[105];struct Point  //存点的信息{    int x, y;    int step;    int id;}p[105];int ex, ey;int dis[105][105];bool vis[105][105];void BFS(Point pt, Point ept){    vis[pt.x][pt.y] = true;    Point now, t;    queue <Point> q;    q.push(pt);    while(!q.empty())    {        now = q.front();        q.pop();        if(now.x == ex && now.y == ey) //记录两点间的最短路        {            dis[pt.id][ept.id] = now.step;            dis[ept.id][pt.id] = now.step;            return;        }        for(int i = 0; i < 4; i ++)        {            t = now;            t.x += dx[i];            t.y += dy[i];            t.step ++;            if(t.x >= n || t.x < 0 || t.y >= m || t.y < 0 || vis[t.x][t.y] || map[t.x][t.y] == 0)                continue;            vis[t.x][t.y] = true;            q.push(t);        }    }}int main(){    while(scanf("%d %d", &n, &m) && (n + m))    {        bool f = false;        int ans = 0;        string next;        char tmp[10];        cnt = 0;        memset(map, 0, sizeof(map));        memset(dis, -1, sizeof(dis));        for(int i = 0; i < n; i++)        {            scanf("%s", s);            for(int j = 0; j < m; j++)            {                if(s[j] == '.')                    map[i][j] = 1;                if(s[j] == '@')                {                    map[i][j] = 3;                    p[cnt].x = i;                    p[cnt].y = j;                    p[cnt].id = cnt;                    p[cnt++].step = 0;                }            }        }        int bx, by;        scanf("%d", &k);        for(int i = 0; i < k; i++)        {            scanf("%d %d", &bx, &by);            if(map[bx - 1][by - 1] == 0)                f = true;            else                map[bx - 1][by - 1] = 2;        }        for(int i = 0; i < n; i++)            for(int j = 0; j < m; j++)                if(map[i][j] == 2)                {                    p[cnt].x = i;                    p[cnt].y = j;                    p[cnt].id = cnt;                    p[cnt++].step = 0;                }  //将起点设置为0点方便后面的操作        for(int i = 0; i < cnt; i++)  //枚举任两点并搜其最短路径        {            for(int j = i + 1; j < cnt; j++)            {                ex = p[j].x;                ey = p[j].y;                memset(vis, false, sizeof(vis));                BFS(p[i],p[j]);            }        }        for(int i = 0; i < cnt; i++) //如果存在宝藏不可达则说明无解        {            for(int j = 0; j < cnt; j++)            {                if(i == j)                continue;                if(dis[i][j] == -1)                {                    f = true;                    break;                }            }            if(f)                break;        }        if(f)        {            printf("-1\n");            continue;        }        //计算每种情况下的总路程        for(int i = 0; i < cnt - 1; i++)            tmp[i] = i + 49;        next.insert(0,tmp,0,cnt-1);        int cur = 0;        for(int i = 0; i < cnt - 1; i++)        {            ans += dis[cur][next[i] - 48];            cur = next[i] - 48;        }        while(next_permutation(next.begin(), next.end())) //举出所有排列        {            int re = 0;            cur = 0;            for(int i = 0; i < cnt - 1; i++)            {                re += dis[cur][next[i] - 48];                cur = next[i] - 48;            }            ans = min(ans, re);        }        printf("%d\n", ans);    }}




0 0