2.1:(穷竭搜索)

https://blog.csdn.net/s_o_n_g_m_i_n_g/article/details/89035175

一.深度优先搜索

1.

Red and Black POJ - 1979 

有一个长方形的房间,覆盖了正方形的磁砖。每块磁砖的颜色,要么是红色,要么是黑色。一名男子站在一块黑色的磁砖上。他可以从一块磁砖移至相邻四块磁砖中的某一块。但是,他不允许在红色磁砖上移动,他只允许在黑色磁砖上移动。 

编写一个程序,使得他允许重复上述的移动,判断他所能到达的黑色磁砖的数量。 

输入

输入由多个数据集组成。数据集的起始行包含了两个正整数 W 和 H;W 和 H 分别是 x- 和 y- 方向的磁砖数量。W 和 H 不超过 20 。 

在数据集中,还有 H 行,每行包含了 W 个字符。每个字符按如下方式表示一块磁砖的颜色。 

'.' - 一块黑色的磁砖 
'#' - 一块红色的磁砖 
'@' - 一名男子,站在一块黑色磁砖上 (在一个数据集中,恰好出现一次) 

以包含两个 0 的一行,表示输入结束。 

输出

对于每个数据集,程序应当输出一行,包含他从初始磁砖所能抵达的磁砖数量 (包括初始磁砖自身)。

示例输入

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

示例输出

45
59
6
13

思路:bfs()即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

int n,m;
char a[25][25];
int ans;
bool used[25][25];
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};
int sx,sy;

void dfs(int x,int y)
{
    ans++;
    for(int i=0;i<4;i++)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if(nx>=0&&nx<n&&ny>=0&&ny<m&&a[nx][ny]=='.'&&!used[nx][ny])
        {
            used[nx][ny]=true;
            dfs(nx,ny);
        }
    }
}

int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(used,0,sizeof(used));
        ans=0;
        if(!m&&!n)
            break;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                cin>>a[i][j];
                if(a[i][j]=='@')
                {
                    sx=i,sy=j;
                }
            }
        }
        dfs(sx,sy);
        printf("%d\n",ans);
    }
    return 0;
}

2.

Property Distribution Aizu - 0118

タナカ氏が HW アールの果樹園を残して亡くなりました。果樹園は東西南北方向に H × W の区画に分けられ、区画ごとにリンゴ、カキ、ミカンが植えられています。タナカ氏はこんな遺言を残していました。

果樹園は区画単位でできるだけ多くの血縁者に分けること。ただし、ある区画の東西南北どれかの方向にとなりあう区画に同じ種類の果物が植えられていた場合は、区画の境界が分からないのでそれらは 1 つの大きな区画として扱うこと。

例えば次のような 3 × 10 の区画であれば ('リ'はリンゴ、'カ'はカキ、'ミ'はミカンを表す)

 

同じ樹がある区画の間の境界を消すと次のようになり、

 

結局 10 個の区画、つまり 10 人で分けられることになります。

雪が降って区画の境界が見えなくなる前に分配を終えなくてはなりません。あなたの仕事は果樹園の地図をもとに分配する区画の数を決めることです。

果樹園の地図を読み込み、分配を受けられる血縁者の人数を出力するプログラムを作成してください。

Input

複数のデータセットが与えられます。各データセットは空白で区切られた H, W (H, W ≤ 100) を含む行から始まり、続いて H × W の文字からなる H 行の文字列が与えられます。この文字列には、リンゴを表す '@'、カキを表す '#'、ミカンを表す '*'、の 3 文字しか現れません。

入力はゼロが2つの行で終わります。データセットの数は 20 を超えません。

Output

各データセットごとに、分配を受ける人数を1行に出力してください。

Sample Input

10 10
####*****@
@#@@@@#*#*
@##***@@@*
#****#*@**
##@*#@@*##
*@@@@*@@@#
***#@*@##*
*@@@*@@##@
*@*#*@##**
@****#@@#@
0 0

Output for the Sample Input

33

转: 题意:在H * W的矩形果园里有苹果、梨、蜜柑三种果树, 相邻(上下左右)的同种果树属于同一个区域,给出果园的果树分布,求总共有多少个区域。 (原题的样图中苹果为リ,梨为カ,蜜柑为ミ, 图中共10个区域) 输入:多组数据,每组数据第一行为两个整数H、W(H <= 100, W <= 100), H =0 且 W = 0代表输入结束。以下H行W列表示果园的果树分布, 苹果是@,梨是#, 蜜柑是*。 输出:对于每组数据,输出其区域的个数

思路:dfs()判断和四周的字符是否相等,以及是否被访问过即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

int n,m;
char a[105][105];
int ans;
bool used[105][105];
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};

void dfs(int x,int y)
{
    used[x][y]=true;
    for(int i=0;i<4;i++)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if(nx>=0&&nx<n&&ny>=0&&ny<m&&!used[nx][ny]&&a[nx][ny]==a[x][y])
        {
            used[nx][ny]=true;
            dfs(nx,ny);
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(!n&&!m)
            break;
        ans=0;
        memset(a,0,sizeof(a));
        memset(used,0,sizeof(used));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                cin>>a[i][j];
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(!used[i][j])
                {
                    used[i][j]=true;
                    dfs(i,j);
                    ans++;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

3.

Ball Aizu - 0033 

如图所示,容器中间有一枢轴,下方分为两路。容器上方开口,从1到10连续编号的小球从容器开口A放入。通过调整枢轴E的方向,可以使小球经过D而落入左侧B筒或者右侧C筒。现给出从A放入小球的顺序,请你判断能否最终小球落入B和C时,号码大的球总是位于号码小的球的上侧。如果可能则在一行中输出”YES”,否则输出 “NO”

Input

第一行一个整数N(N<1000)
接下来有N行,每行10个由空格分隔的[1,10]区间内的整数,分别代表10个小球放入A筒的顺序

Output

对每组数据,输出一行YES或NO

Sample

Sample Input

2
3 1 4 2 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1

Output for the Sample Input

YES
NO

思路:把两个筒的最后一个分别设为l和r如果后面的数比l和r都小就输出no

#include<bits/stdc++.h>
using namespace std;

int a[15];

void dfs(int l,int r,int k)
{
	if(k==10)
	{
		cout<<"YES"<<endl;
		return ;
	}
	if(a[k]>l)
		dfs(a[k],r,k+1);
	else if(a[k]>r)
		dfs(l,a[k],k+1);
	else
	{
		cout<<"NO"<<endl;
		return ;
	}
}

int main()
{
	int N;
	scanf("%d",&N);
	while(N--)
	{
		for(int i=0;i<10;i++)
		{
			scanf("%d",&a[i]);
		}
		dfs(0,0,0);
	}
}

4.

Curling 2.0 POJ - 3009 

今年的奥运会之后,在行星mm-21上冰壶越来越受欢迎。但是规则和我们的有点不同。这个游戏是在一个冰游戏板上玩的,上面有一个正方形网格。他们只用一块石头。游戏的目的是让石子从起点到终点,并且移动的次数最小

图1显示了一个游戏板的例子。一些正方形格子可能被砖块占据。有两个特殊的格子,起始点和目标点,这是不占用块。(这两个方块是不同的)一旦石头开始移动就不会停下,除非它击中砖块块。为了使石头到达终点,你可以通过让石块击中墙壁或者砖块来停下。


图1:例子(S:开始,G:目标)

石头的运动遵循以下规则:

  • 开始时,石头静止起点广场上。
  • 石头的运动仅限于x和y方向。禁止对角线移动。
  • 当石头静止时,你可以让他向任意方向移动,除非它移动的方向上有砖块(图2(a))。
  • 一旦抛出,石头不断向同一方向移动,直到下列事件之一发生:
    • 石头击中砖块(图2(b),(c))。.
      • 石头停在他击中的砖块之前
      • 被击中的砖块消失
    • 石块飞出游戏板之外。
      • 游戏结束的条件
    • 到达目标点
      • 石头停在目标点游戏成功
  • 不能在十步之内到达目标点则返回失败。


Fig. 2: Stone movements

通过这些规则我们想知道,石头是否能够到达目标点和最少移动次数

初始配置如图1所示,石头从开始到目标需要4次移动。路线如图3所示(a)。注意当石头到达目标时,游戏版的配置如图3(b)改变。


图3:图1的解决方案和解决之后的结果。

Input

输入是一组数据。输入结束标志为两个0。数据组的数量不超过100。

每个数据集如下展示

板的宽度(w)和高度(h) 
游戏版的第一行 
... 
游戏版的h-th行

版的宽和高满足: 2 <= w <= 20, 1 <= h <= 20.

每行由一个空格分隔的十进制数字组成。该数字描述相应的格子的状态。

0
1砖块
2开始点
3目标点

图. D-1数据如下:

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

Output

对于每个数据,打印一个十进制整数的行,表示从开始到目标的路径的最小移动次数。如果没有这样的路线,打印- 1。每个行不应该有这个数字以外的任何字符。

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1

思路:dfs()+回溯 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int INF=0x3f3f3f3f;
int n,m;
int a[25][25];
int minstep;
int sx,sy;
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};

void dfs(int x,int y,int step)
{
    if(step>10)
        return ;
    for(int i=0;i<4;i++)
    {
        int flag=0;
        int nx=x+dx[i],ny=y+dy[i];
        while(nx>=0&&nx<n&&ny>=0&&ny<m&&a[nx][ny]!=1)
        {
            flag=1;
            if(a[nx][ny]==3)
                minstep=min(step,minstep);
            nx+=dx[i],ny+=dy[i];
        }
        if(nx>=0&&nx<n&&ny>=0&&ny<m&&flag&&a[nx][ny]==1)
        {
            a[nx][ny]=0;
            dfs(nx-dx[i],ny-dy[i],step+1);
            a[nx][ny]=1;
        }
    }
}

int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        if(!m&&!n)
            break;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%d",&a[i][j]);
                if(a[i][j]==2)
                {
                    sx=i;
                    sy=j;
                }
            }
        }
        minstep=INF;
        dfs(sx,sy,1);
        if(minstep==INF)
            puts("-1");
        else
            printf("%d\n",minstep);
    }
    return 0;
}

二.广度优先搜索

1.

Cheese  Aizu - 0558 

チーズ ()

問題

在H * W的地图上有N个奶酪工厂,每个工厂分别生产硬度为1-N的奶酪。有一只老鼠准备从出发点吃遍每一个工厂的奶酪。老鼠有一个体力值,初始时为1,每吃一个工厂的奶酪体力值增加1(每个工厂只能吃一次),且老鼠只能吃硬度不大于当前体力值的奶酪。 老鼠从当前格到上下左右相邻的无障碍物的格需要时间1单位,有障碍物的格不能走。走到工厂上时即可吃到该工厂的奶酪,吃奶酪时间不计。问吃遍所有奶酪最少用时

入出力例

输入:第一行三个整数H(1 <= H <= 1000)、W(1 <= W <=1000)、N(1 <= N <= 9),之后H行W列为地图, “.“为空地, ”X“为障碍物,”S“为老鼠洞,N表示有N个生产奶酪的工厂,硬度为1-N。

入力例 1

3 3 1
S..
...
..1

出力例 1

4

入力例 2

4 5 2
.X..1
....X
.XX.S
.2.X.

出力例 2

12

入力例 3

10 10 9
.X...X.S.X
6..5X..X1X
...XXXX..X
X..9X...X.
8.X2X..X3X
...XX.X4..
XX....7X..
X..X..XX..
X...X.XX..
..X.......

出力例 3

91

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

思路:求各个点到下一个点的最小距离相加即可 bfs()

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>

using namespace std;

const int INF=0x3f3f3f3f;
int n,m,N;
char a[1005][1005];
int sx,sy;
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};
typedef pair<int,int> P;
int d[1005][1005];

void bfs(int x,int y)
{

    long long sum=0;
    for(int i=0;i<N;i++)
    {
        int level=i+1;
        for(int i=0;i<1005;i++)
        {
            for(int j=0;j<1005;j++)
                d[i][j]=INF;
        }
        d[x][y]=0;
        queue<P> que;
        que.push(P(x,y));
        while(!que.empty())
        {
            P p=que.front();
            que.pop();
            if(a[p.first][p.second]-'0'==level)
            {
                x=p.first,y=p.second;
                sum+=d[p.first][p.second];
                break;
            }
            for(int j=0;j<4;j++)
            {
                int nx=p.first+dx[j],ny=p.second+dy[j];
                if(nx>=0&&nx<n&&ny>=0&&ny<m&&d[nx][ny]==INF&&a[nx][ny]!='X')
                {
                    que.push(P(nx,ny));
                    d[nx][ny]=d[p.first][p.second]+1;
                }
            }
        }
    }
    printf("%d\n",sum);
}

int main()
{
    scanf("%d%d",&n,&m);
    scanf("%d",&N);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>a[i][j];
            if(a[i][j]=='S')
            {
                sx=i;
                sy=j;
            }
        }
    }
    bfs(sx,sy);
    return 0;
}

2.

Meteor Shower POJ - 3669

Description - 题目描述

Bessie听说有场史无前例的流星雨即将来临;有谶言:陨星将落,徒留灰烬。为保生机,她誓将找寻安全之所(永避星坠之地)。目前她正在平面坐标系的原点放牧,打算在群星断其生路前转移至安全地点。

此次共有M (1 ≤ M ≤ 50,000)颗流星来袭,流星i将在时间点Ti (0 ≤ Ti  ≤ 1,000) 袭击点 (Xi, Yi) (0 ≤ Xi ≤ 300; 0 ≤ Yi ≤ 300)。每颗流星都将摧毁落点及其相邻四点的区域。

Bessie在0时刻时处于原点,且只能行于第一象限,以平行与坐标轴每秒一个单位长度的速度奔走于未被毁坏的相邻(通常为4)点上。在某点被摧毁的刹那及其往后的时刻,她都无法进入该点。

寻找Bessie到达安全地点所需的最短时间。

Input - 输入

* 第1行: 一个整数: M
* 第2..M+1行: 第i+1行包含由空格分隔的三个整数: Xi, Yi, and Ti

Output - 输出

* 仅一行: Bessie寻得安全点所花费的最短时间,无解则为-1。

Sample Input - 输入样例

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

Sample Output - 输出样例

 

思路:题解:首先要建图,用map[][]数组储存好每个点遭受流星打击的时间,不受流星打击的用-1涵盖。(不能用0覆盖,部分点可能在0时刻遭受打击) 然后遍历整张图,找到不受流星打击的点即可,因为要求最短时间,所有要用到优先队列,优先时间最小的点出队列。根据题意我们知道在图内的不受流星打击的点和没有经过流星打击的点能进入队列。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

struct node
{
	int x;
	int y;
	int step;
}a,temp;

bool operator < (const node &a,const node &b)
{
	return a.step>b.step;
}

int map[500][500];
int mark[500][500];
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};

void bfs()
{
	priority_queue<node> que;
	a.x=0;
	a.y=0;
	a.step=0;
	que.push(a);
	mark[0][0]=1;
	while(!que.empty())
	{
		node q=que.top();
		que.pop();
		if(map[q.x][q.y]==-1)
		{
			printf("%d\n",q.step);
			return ;
		}
		for(int i=0;i<4;i++) 
		{
			int nx=q.x+dx[i],ny=q.y+dy[i];
			if(nx>=0&&ny>=0&&(map[nx][ny]==-1||q.step+1<map[nx][ny])&&!mark[nx][ny])
			{
				mark[nx][ny]=1;
				temp.x=nx,temp.y=ny,temp.step=q.step+1;
				que.push(temp);
			}
		}
	}
	printf("-1");
}

int main()
{
	int n,x,y,t;
	memset(map,-1,sizeof(map));
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d%d",&x,&y,&t);
		if(map[x][y]==-1)
			map[x][y]=t;
		else
			map[x][y]=min(map[x][y],t);
		for(int j=0;j<4;j++)
		{
			int nx=dx[j]+x,ny=dy[j]+y;
			if(nx>=0&&ny>=0)
			{
				if(map[nx][ny]==-1)
					map[nx][ny]=t;
				else
					map[nx][ny]=min(map[nx][ny],t);
			}
		}
	}
	bfs();
	return 0;
} 

3.

Seven Puzzle Aizu - 0121

7 パズル

7 パズルは 8 つの正方形のカードとこれらのカードがぴたりと収まる枠で構成されています。それぞれのカードには、互いに区別できるように 0, 1, 2, ..., 7 と番号がつけられています。枠には、縦に 2 個、横に 4 個のカードを並べることができます。

7 パズルを始めるときには、まず枠にすべてのカードを入れます。枠のなかで 0 のカードだけは、上下左右に隣接するカードと位置を交換することができます。たとえば、枠の状態が図(a) のときに、0 のカードの右に隣接した、7 のカードと位置を交換すれば、図(b) の状態になります。あるいは、図(a) の状態から 0 のカードの下に隣接した 2 のカードと位置を交換すれば図(c) の状態になります。図(a) の状態で 0 のカードと上下左右に隣接するカードは 7 と 2 のカードだけなので、これ以外の位置の入れ替えは許されません。

ゲームの目的は、カードをきれいに整列して図(d) の状態にすることです。最初の状態を入力とし、カードをきれいに整列するまでに、必要な最小手数を出力するプログラムを作成してください。ただし、入力されたカードの状態からは図(d) の状態に移ることは可能であるとします。

入力データは、1 行に 8 つの数字が空白区切りで与えられます。これらは、最初の状態のカードの並びを表します。例えば、図(a) の数字表現は0 7 3 4 2 5 1 6 に、図(c) は 2 7 3 4 0 5 1 6 となります。

図(a) 0 7 3 4 2 5 1 6 の場合図(b) 7 0 3 4 2 5 1 6 の場合

 

図(c) 2 7 3 4 0 5 1 6 の場合図(d) 0 1 2 3 4 5 6 7 (最終状態)

 

Input

上記形式で複数のパズルが与えられます。入力の最後まで処理してください。 与えられるパズルの数は 1,000 以下です。

Output

各パズルについて、最終状態へ移行する最小手数を1行に出力してください。

Sample Input

0 1 2 3 4 5 6 7
1 0 2 3 4 5 6 7
7 6 5 4 3 2 1 0

Output for the Sample Input

0
1
28

题意:7数码问题。在2*4的棋盘上,摆有7个棋子,每个棋子上标有1到7的某一个数字,不同棋子上的数字不相同。棋盘还有一个空格,与空格相邻(上下左右)的棋子可以移动到空格中,该棋子原先位置成为空格。给出一个初始状态(保证可以转移到最终状态),找出一种从初始状态转变到给定最终状态的移动棋子最小的步数。
 

思路:思路:和一般的bfs不同,这题的bfs没有明确的移动对象,看似任意一个数都可以当成对象移动。这是我们只需要抓住一个格子就行,比如格子0.由于题目的输入是多个case,每个case都运行一遍bfs就会TLE,不过这题和图的最短路径(不考虑权值)一样,只需要运用dp的思想,一次bfs就可以解决,以后每个case 到解空间里去找就行了。
采取逆向思维,可以用广度优先搜索解决。先不考虑如何用最小步数从初始状态到最终状态,所有结果的最终状态都是(01234567),那么反过来想只要记录最终状态到所有结果的最小步数,接下来查表即可。(求最小步数和图的最短路径一样,不考虑权值)。用map<string,int>表示(01234567)到string的最小步数int,只要当前结果还未记录,就加入map,直到穷尽所有状态。另外,假设p是当前状态0所在的位置,那么移动方向是上下左右后0所在的位置就是+4,-4,-1,+1.需要注意有些位置不能向某个方向移动。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<cstring>
using namespace std;

map<string,int>dp;
int direction[]={1,-1,-4,4};

void bfs()
{
	queue<string> que;
	que.push("01234567");
	dp["01234567"]=0;
	while(!que.empty())
	{
		string q=que.front();
		que.pop();
		int k=0;
		for(int i=0;i<8;i++)
		{
			if(q[i]=='0')
			{
				k=i;
				break;
			}
		}
		for(int i=0;i<4;i++)
		{
			int n=k+direction[i];
			if(n>=0&&n<8&&!(k==3&&i==0)&&!(k==4&&i==1))
			{
				string s=q;
				swap(s[n],s[k]);
				if(dp.find(s)==dp.end())
				{
					dp[s]=dp[q]+1;
					que.push(s);
				}
			}
		}
	}
}

int main()
{
	bfs();
	string s;
	while(getline(cin,s))
	{
		s.erase(remove(s.begin(),s.end(),' '),s.end());
		printf("%d\n",dp[s]);
	} 
	return 0;
}

三.穷竭搜索

1.

Smallest Difference  POJ - 2718 

Description - 题目描述

给定若干位十进制数,你可以通过选择一个非空子集并以某种顺序构建一个数。剩余元素可以用相同规则构建第二个数。除非构造的数恰好为0,否则不能以0打头。 

举例来说,给定数字0,1,2,4,6与7,你可以写出10和2467。当然写法多样:210和764,204和176,等等。最后一对数差的绝对值为28,实际上没有其他对拥有更小的差。

Input - 输入

输入第一行的数表示随后测试用例的数量。
对于每组测试用例,有一行至少两个不超过10的十进制数字。(十进制数字为0,1,…,9)每行输入中均无重复的数字。数字为升序给出,相隔恰好一个空格。

Output - 输出

对于每组测试用例,输出一个以上述规则可获得的最小的差的绝对值在一行。

Sample Input - 输入样例

1
0 1 2 4 6 7

Sample Output - 输出样例

28
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;

const int INF=0x3f3f3f3f;

int main()
{
	int N;
	scanf("%d",&N);
	getchar();
	int a[15];
	int ans;
	while(N--)
	{
		memset(a,0,sizeof(a));
		ans=INF;
		string s;
		getline(cin,s);
		s.erase(remove(s.begin(),s.end(),' '),s.end());
		int n=0;
		for(int i=0;i<s.length();i++)
		{
			a[n++]=s[i]-'0';
		}
		if(n==2)
		{
			cout<<a[1]-a[0]<<endl;
			continue;
		}
		do
		{
			int mid=(n+1)/2;
			if(a[0]==0||a[mid]==0)
				continue;
			int sum1=0,sum2=0;
			for(int i=0;i<mid;i++)
			{
				sum1=sum1*10+a[i];
			}
			for(int i=mid;i<n;i++)
			{
				sum2=sum2*10+a[i];
			}
			ans=min(ans,abs(sum1-sum2));
		}while(next_permutation(a,a+n));
		cout<<ans<<endl;
	}
	return 0;
} 

2.

Backward Digit Sum  POJ - 3187 

FJ 和 他的奶牛们在玩一个心理游戏。他们以某种方式写下1至N的数字(1<=N<=10)。 然后把相邻的数相加的到新的一行数。重复这一操作直至只剩一个数字。比如下面是N=4时的一种例子 

    3   1   2   4

      4   3   6

        7   9

         16

在FJ回来之前,奶牛们开始了一个更难的游戏:他们尝试根据最后结果找到开始的序列。这已超过了FJ的思考极限。 

写一个程序来帮助FJ吧

Input

N和最后的和

Output

满足要求的一行序列。若有多种情况,输出字典序最小的一种

Sample Input

4 16

Sample Output

3 1 2 4

Hint

样例解释 

这里还有其他可能的排列,若 3 2 1 4,但 3 1 2 4 是字典序最小的

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

int a[10]={1,2,3,4,5,6,7,8,9,10};
int N,sum;

int main()
{
	scanf("%d%d",&N,&sum);
	do
	{
		int c[15];
		for(int i=0;i<N;i++)
		{
			c[i]=a[i];
		}
		int b=N;
		while(b!=1)
		{
			for(int i=0;i<b-1;i++)
			{
				c[i]=c[i]+c[i+1];
			}
			b--;
		}
		if(c[0]==sum)
		{
			for(int i=0;i<N;i++)
			{
				cout<<a[i]<<" ";
			}
			cout<<endl;
			break;
		}
	}while(next_permutation(a,a+N));
	return 0;
}

3.

Hopscotch POJ - 3050 

在5*5的格子里,填充着各种个位数,现在可以从任意点出发,上下左右移动,可以重复走过的点,每次只能走5步。问经过的格子中组成的6位数(允许有前导零的存在)有多少种?

Input

输入一个5*5的网格,每格1个1位整数。

Output

输出只有一行,为可以构造的6位数的数目。

Sample Input

1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 2 1
1 1 1 1 1

Sample Output

15

Hint

   OUTPUT DETAILS: 
   111111, 111112, 111121, 111211, 111212, 112111, 112121, 121111, 121112, 121211, 121212, 211111, 211121, 212111, and 212121 can be constructed. No other values are possible.

思路:dfs+set去重

 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<sstream>

using namespace std;

typedef pair<int,int> P;
int map[5][5];
int a[6];
set<int> set1;
int dx[4]={0,-1,1,0},dy[4]={-1,0,0,1};

void dfs(int x,int y,int k)
{
	if(k==6)
	{
		int sum=0;
		for(int i=5;i>=0;i--)
		{
			sum=sum*10+a[i];
		}
		set1.insert(sum);
		return ;
	}
	for(int i=0;i<4;i++)
	{
		int nx=dx[i]+x,ny=dy[i]+y;
		if(nx>=0&&nx<5&&ny>=0&&ny<5)
		{
			a[k+1]=map[nx][ny];
			dfs(nx,ny,k+1);
		}
	}
}

int main()
{
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
			scanf("%d",&map[i][j]);
	}
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			a[0]=map[i][j];
			dfs(i,j,0);
		}
	}
	cout<<set1.size()<<endl;
	return 0;
} 

4.

Osenbei  Aizu - 0525 

おせんべい

問題

IOI製菓では,創業以来の伝統の製法で煎餅(せんべい)を焼いている.この伝統の製法は,炭火で一定時間表側を焼き,表側が焼けると裏返して,炭火で一定時間裏側を焼くというものである.この伝統を守りつつ,煎餅を機械で焼いている.この機械は縦 R (1 ≤ R ≤ 10) 行, 横 C (1 ≤ C ≤ 10000) 列の長方形状に煎餅を並べて焼く.通常は自動運転で,表側が焼けたら一斉に煎餅を裏返し裏側を焼く.

ある日,煎餅を焼いていると,煎餅を裏返す直前に地震が起こり何枚かの煎餅が裏返ってしまった.幸いなことに炭火の状態は適切なままであったが,これ以上表側を焼くと創業以来の伝統で定められている焼き時間を超えてしまい,煎餅の表側が焼けすぎて商品として出荷できなくなる.そこで,急いで機械をマニュアル操作に変更し,まだ裏返っていない煎餅だけを裏返そうとした.この機械は,横の行を何行か同時に裏返したり縦の列を何列か同時に裏返したりすることはできるが,残念なことに,煎餅を1枚ごと裏返すことはできない.

裏返すのに時間がかかると,地震で裏返らなかった煎餅の表側が焼けすぎて商品として出荷できなくなるので,横の何行かを同時に1回裏返し,引き続き,縦の何列かを同時に1回裏返して,表側を焼きすぎずに両面を焼くことのできる煎餅,つまり,「出荷できる煎餅」の枚数をなるべく多くすることにした.横の行を1行も裏返さない,あるいは,縦の列を1列も裏返さない場合も考えることにする.出荷できる煎餅の枚数の最大値を出力するプログラムを書きなさい.

地震の直後に,煎餅が次の図のような状態になったとする.黒い丸が表側が焼ける状態を,白い丸が裏側が焼ける状態を表している.

1行目を裏返すと次の図のような状態になる.

さらに, 1列目と5列目を裏返すと次の図のような状態になる.この状態では,出荷できる煎餅は9枚である.

ヒント

R の上限 10 は C の上限 10000 に比べて小さいことに注意せよ.

入力

入力は複数のデータセットからなる.各データセットは以下の形式で与えられる.

入力の1行目には2つの整数 R, C (1 ≤ R ≤ 10, 1 ≤ C ≤ 10 000) が空白を区切りとして書かれている.続く R 行は地震直後の煎餅の状態を表す. (i+1) 行目 (1 ≤ i ≤ R) には, C 個の整数 ai,1, ai,2, ……, ai,C が空白を区切りとして書かれており, ai,j は i 行 j 列 の煎餅の状態を表している. ai,j が 1 なら表側が焼けることを, 0 なら裏側が焼けることを表す.

C, R がともに 0 のとき入力の終了を示す. データセットの数は 5 を超えない.

出力

データセットごとに,出荷できる煎餅の最大枚数を1行に出力する.

入出力例

入力例

2 5
0 1 0 1 0
1 0 0 0 1
3 6
1 0 0 0 1 0
1 1 1 0 1 0
1 0 1 1 0 1  
0 0

出力例

9
15

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

题意:题目大意是说,有一个煎饼器,可以烤R行C列的煎饼,煎饼可以正面朝上(用1表示),也可以背面朝上(用0表示),一次可以翻转一整行或一整列的煎饼。现在需要把尽量多的煎饼翻成正面朝上,给定煎饼的当前状态,问经过翻转后,最多能使多少煎饼正面朝上。

思路:

因为列数很大,行数只有10,所以枚举出10行所有的情况只有210=1024210=1024 种可能性。行翻转完毕后,统计每一行朝上的和朝下的个数,取最大值即可。 

用bitset来表示每一行的情况,所以就很容易表达出来了

#include<bits/stdc++.h>
using namespace std;

int sum;
int n,m;
bitset<10005> a[15]; 
int ans;

void dfs(int k)
{
	if(k==n)
	{
		int res=0;
		for(int i=0;i<m;i++)
		{
			int num=0;
			for(int j=0;j<n;j++)
			{
				if(a[j][i]==1)
					num++;
			}
			res+=max(num,n-num);	
		}
		ans=max(ans,res);
		return ;
	}
	dfs(k+1);
	a[k].flip();
	dfs(k+1);
}

int main()
{
	while(scanf("%d%d",&n,&m))
	{
		if(!n&&!m)
			break;
		ans=0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				bool temp;
				scanf("%d",&temp);
				a[i][j]=temp;
			}
				
		}
		dfs(0);
		cout<<ans<<endl;
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值