首页 > 代码库 > 基础搜索算法题解(N-R)

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

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


N题 Shredding Company

Time Limit: 1000MS
Memory Limit: 10000K
Total 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 12346
376 144139
927438 927438
18 3312
9 3142
25 1299
111 33333
103 862150
6 1104
0 0

Sample Output

43 1 2 34 6
283 144 139
927438 927438
18 3 3 12
error
21 1 2 9 9
rejected
103 86 2 15 0
rejected

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: 65536K
Total 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

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

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: 10000K
Total 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

2
A:
B:
4
A:BC
B:ACD
C:ABD
D:BC
4
A:BCD
B:ACD
C:ABD
D:ABC
0

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~

<span style="font-size:14px;">#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");
 }</span>


R题



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