新题库所有题目的汇总

1.汽车加油(马拉松跑步)
//给5种速度和时间(分秒),距离D,体能H,体能不用完的情况下,最快多久(分钟)走完D距离
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int D, H, Time[5], En[5], answer;
void dfs(int index,int dis,int costTime,int en)//从速度0开始取,剩下的距离、花费的时间、剩下的体能
{
	if (costTime > answer)return;
	if (dis == 0)
	{
		if (answer > costTime)
		{
			answer = costTime;
		}
		return;
	}
	for (int i = index; i < 5;i++)//继续选当前节点
	{
		if (en - En[i] < 0)continue;
		dfs(i,dis-1,costTime+Time[i],en-En[i]);
		index++;
	}
}
int main()
{
	int T,x,y;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{
		scanf("%d%d",&H,&D);
		for (int i = 0; i < 5;i++)
		{
			scanf("%d%d%d",&x,&y,&En[i]);
			Time[i] = x*60 + y;
		}
		answer = 99999999;
		dfs(0,D,0,H);
		printf("#%d %d %d\n", tc, answer/60,answer%60);
	}
	return 0;
}

/*
input:
1

130 30
4 50 7
5 0 5
5 10 4
5 20 3
5 30 2

output:
#1 153 20
*/


2、到点的最大概率
//从节点1出发,每10分钟移动一次,0-9分钟在1号,10-19分钟在2号概率0.3,在3号概率0.7
//给定各点连接关系与时间,问在哪个节点的概率最大
//当访问到一个出度为0的节点的时候,10分钟后将离开图,不在任何节点
//思想:需要一个数组存当前节点指向的节点们,每过10分钟就遍历一遍这些节点
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, V, Time;
int pos;//最大概率的点
float maxpos[100],answer;//到时间在某个点,最大概率
struct Nodes
{
	int cnt;//指向的节点的个数
	int next[100];//指向的节点们
	float t[100];//指向的节点的概率
};
Nodes node[100];
//dfs(1,Time,1);Time求解时间,每次时间-10
void dfs(int n,int deep,float nowpos)//从节点n开始,还剩多少时间,当前的概率
{
	if (deep == 0) //走到头了
	{
		maxpos[n] += nowpos;//可能多条路都走到了
		if (maxpos[n] > answer)
		{
			pos = n;
			answer = maxpos[n];
		}
		return;
	}
	//遍历当前节点指向的节点们
	for (int i = 0; i < node[n].cnt;i++)
	{
		dfs(node[n].next[i],deep-10,nowpos*node[n].t[i]);
	}
}
int main()
{
	int T, x, y;
	float time;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{
		scanf("%d%d%d",&N,&V,&Time);//节点数、边数、求解时间
		for (int i = 1; i <= N;i++)
		{
			node[i].cnt = 0;
			maxpos[i] = 0;
		}
		for (int i = 0; i < V;i++)//V条边
		{
			scanf("%d%d%f",&x,&y,&time);//x到y的概率是time
			node[x].next[node[x].cnt] = y;
			node[x].t[node[x].cnt++] = time;
		}
		answer = 0;//求最大,初始化为最小
		pos = 0;
		dfs(1,Time,1);
		printf("#%d %d %f\n", tc, pos,answer);
	}
	return 0;
}

/*
input:
1
6 10 40
1 2 0.3
1 3 0.7
3 3 0.2
2 4 1.0
3 4 0.8
4 4 0.1
4 5 0.9
5 6 1.0
6 6 0.5
6 3 0.5

output:
#1 6 0.774000
*/




3、虫洞
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

#define MAX 100
#define MAXN 5
int N;//N个虫洞
int sx, sy, dx, dy;//起点和终点的坐标
struct node //虫洞(起点、终点的坐标,穿越时间)
{
	int x1;
	int y1;
	int x2;
	int y2;
	int time;
};
node datas[MAXN];
bool visit[MAXN];
int minD; //最短穿越距离
int comDis(int x1, int y1, int x2, int y2)
{
	return ((x1>x2) ? x1 - x2 : x2 - x1) + ((y1>y2) ? y1 - y2 : y2 - y1);
}
//当前穿了第step个虫洞,需要穿num个虫洞,当前坐标(sx,xy),当前已经走了距离distance
void dfs(int step, int num, int sx, int sy, int distance)
{
	if (distance >= minD)
		return;
	if (step == num)//走了num个虫洞
	{
		int dis = distance + comDis(sx, sy, dx, dy);//当前坐标到终点是直接走过去的,没有虫洞可以穿越
		minD = (dis < minD) ? dis : minD;
		return;
	}
	for (int i = 0; i<N; ++i)
	{
		if (visit[i]) continue;
		visit[i] = true;
		//从当前位置到虫洞1的1号位置,穿越后到2号位置,继续dfs找虫洞
		//当前距离+当前坐标到虫洞的端点1的距离+穿越虫洞时间
		int dis1 = distance + comDis(sx, sy, datas[i].x1, datas[i].y1) + datas[i].time;
		dfs(step + 1, num, datas[i].x2, datas[i].y2, dis1);
		//从当前位置到虫洞1的2号位置,穿越后到1号位置,继续dfs找虫洞
		int dis2 = distance + comDis(sx, sy, datas[i].x2, datas[i].y2) + datas[i].time;
		dfs(step + 1, num, datas[i].x1, datas[i].y1, dis2);
		visit[i] = false; //回溯,不走这个虫洞,换下一个虫洞试试看
	}
}

int main(int argc, char** argv)
{
	int tc;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (tc = 1; tc <= T; tc++)
	{

		cin >> N;
		cin >> sx >> sy >> dx >> dy;//起点和终点
		for (int i = 0; i < N; i++)//N个虫洞
		{
			cin >> datas[i].x1 >> datas[i].y1 >> datas[i].x2 >> datas[i].y2 >> datas[i].time;
		}
		minD = comDis(sx, sy, dx, dy);//初始化最短距离是不穿越虫洞,直接起点到终点,相当于穿越0个虫洞
		for (int i = 0; i<N; ++i) //用i来控制穿越虫洞数量,不然dfs没头,穿1个、2个……N个虫洞
		{
			dfs(0, i + 1, sx, sy, 0);
		}
		cout << '#' << tc << ' ' << minD << endl;
	}
	return 0;
}
/*
input:
5

0
0 0 60 60

1
0 0 2 0
1 0 1 2 0

1
0 0 60 60
40 40 20 20 10

2
100 50 10 5
80 40 10 6 10
80 10 70 40 5

3
500 500 1000 1000
501 501 999 999 1000
1 1 499 499 100
1000 999 0 0 200

output:
#1 120
#2 2
#3 90
#4 41
#5 305
*/



4、岛屿面积(找1的方块有多少个)
//大小为m*n的矩阵grid,岛屿是相邻的1组成的(水平或竖直4个方向相邻),假设grid的4个边远都被0(水)包围着
//岛屿的面积是岛上值为1的单元格的数组
//计算病范围grid中最大的岛屿面积,如果没有岛屿,则返回面积为0
太简单了啊,for找到一个1就dfs(),count+1,dfs的时候走过的1全变成0




5、新冠病毒人数
//给定一张图N*N,0代表空地,1代表一个人,2代表新冠病毒所在地
//新冠病毒数量>=1,新冠病毒只能(上下左右)扩散到1(人),扩散到人的地方,人变成新的传染源(2),新的继续传播
//最少经过多少天以后,剩余的人数保持不变?输出天数,剩余人数
思路:BFS,input的时候遇见2就进队,然后出队,2周围的1进队,进队后变成2,没终止条件,全部走完没东西进队,周围全0了,终止
step标记第几步就是第几天感染
剩余人数=遍历map,只剩1 2 0 ,1就是剩余人数,count
//新冠病毒检测 ,N在5到10之间
//N*N,0空地,1人,2新冠病毒,新冠只能扩散到1,人变成新的新冠传播源,继续扩散,最少多少天,剩余人数不变
//输出 天数 剩余人数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, answer,ans_people,map[10][10];
int qx[1000], qy[1000], qday[1000];
int head,tail;
int d[4][2] = { {1,0},{-1,0},{0,1},{0,-1} };
void add(int x, int y, int day)
{
    qx[tail] = x;
    qy[tail] = y;
    qday[tail] = day;
    tail++;
}
int main()
{
    freopen("input.txt", "r", stdin);
    int t;
    scanf("%d", &t);
    for (int tc = 1; tc <= t; tc++)
    {
        scanf("%d",&N);
        head = 0, tail = 0;
        for (int i = 0; i < N;i++)
        {
            for (int j = 0; j < N;j++)
            {
                scanf("%d",&map[i][j]);
                if (map[i][j] == 2) //所有火源进队
                {
                    add(i, j, 0);
                }
            }
        }
        answer = 0;
        while (head<tail)
        {
            int currx = qx[head]; //出队
            int curry = qy[head];
            int currday = qday[head];
            head++;
            //如果上下左右都是0,那就说明走到头
            for (int i = 0; i < 4;i++)//四周蔓延
            {
                int xNext = currx + d[i][0];
                int yNext = curry + d[i][1];
                if (xNext >= 0 && xNext < N&&yNext >= 0 && yNext < N)
                {
                    if (map[xNext][yNext]==1)//不标记是因为进队后1变成2,所以不需要标记
                    {
                        add(xNext, yNext, currday + 1);//进队
                        map[xNext][yNext] = 2;//传播结束变成2
                    }
                }
            }
            if (currday > answer)
            {
                answer = currday;
            }
        }
        ans_people = 0;
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                if (map[i][j] == 1)
                {
                    ans_people++;
                }
            }
        }
        printf("#%d %d %d\n",tc,answer, ans_people);
    }
    return 0;
}
/*
input:
1
6
1 0 1 2 1 2
1 0 1 1 1 1
1 0 2 1 1 1
0 0 1 1 1 1
1 0 1 1 0 0
1 1 2 0 1 1

output:
#1 3 5

*/





5.1、砍树,最多砍掉M棵树,问最后剩下的1最多为多少?
//先bfs再dfs,BFS计算火源周围的树有多少个,烧掉还剩多少,记录下被烧掉的树坐标,visited赋值map
//dfs用来最多砍M棵树,砍树的时候不需要记录被烧掉的树坐标,只需要把之前第一次记录的未砍树的时候火源周围的树的坐标遍历一遍,
//砍不砍回溯一下,每次砍掉一棵树调用一次BFS计算剩下多少棵树,取最大
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int N, M;
int map[11][11];
struct TData {  //存放有2的方块
	int x, y;
};
TData fireList[100];  //存有2的方块的坐标
int fireCnt = 0; //存有2的方块里面坐标的个数
TData fireWaitList[100]; //等待被烧的坐标
int fireWaitCnt;//等待被烧的个数
int treeCnt = 0; //树的个数
int Answer = 0;
int dir[4][2] = { { 1, 0 },{ -1, 0 },{ 0, -1 },{ 0, 1 } }; //4个方向
int getLeftTrees(bool getWaitList, int cutTree) { //砍cutTree棵树后剩下多少棵树
	int head = 0;
	int tail = 0;
	TData mq[101];
	int leTreeCnt = treeCnt - cutTree; //剩下的树=总共的树-被砍掉的树
									   
	int visited[10][10];//初始化visited数组,把map赋值过来
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			visited[i][j] = map[i][j];

	for (int i = 0; i < fireCnt; i++) {  //fireList是火源的坐标,fireCnt是火源数量 先把所有的火源进队
		mq[tail].x = fireList[i].x;   //进队
		mq[tail].y = fireList[i].y;
		tail++;
	}

	//bfs
	while (head<tail) {  //跟火源相邻的所有的树全部进队啊,进一个烧一个,剩下的树少
		if (Answer > leTreeCnt) {
			return Answer;
		}
		//add new fire trees
		for (int i = 0; i < 4; i++) {
			int nx = mq[head].x + dir[i][0];  
			int ny = mq[head].y + dir[i][1];
			if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;//越界
			if (visited[nx][ny] == 1) { //如果是一棵树,会被烧掉变成空地
				mq[tail].x = nx;  
				mq[tail++].y = ny;
				visited[nx][ny] = 0; //变成空地0
				leTreeCnt--; //剩下的树减少
			}
		}
		head++; 
	}
	if (getWaitList) {   //fireWaitList是等待烧的树(火源周围的树)
		for (int i = fireCnt, j = 0; i < tail; i++, j++) { //fireCnt是火源的个数,mq[fireCnt]开始就是火源周围的树
			fireWaitList[j].x = mq[i].x;//出队记录被烧的树
			fireWaitList[j].y = mq[i].y;
			fireWaitCnt++;
		}
	}
	return leTreeCnt; //返回剩下的树=总共的树-砍掉的树-被烧的树
}

void dfs(int cutTreeNum) //还有cutTreeNum棵树可以砍
{
	if (cutTreeNum < M) { //只要小于M说明至少砍过一棵树,砍掉的树个数=M-cutTreeNum
		int tmpCnt = getLeftTrees(0, M - cutTreeNum); //不得到被砍的树,砍掉M-cutTreeNum棵树后计算剩下的树个数
		Answer = Answer > tmpCnt ? Answer : tmpCnt; //答案跟剩下的树的个数取max
	}
	if (cutTreeNum == 0) //还能砍的树为0
		return;
	for (int i = 0; i < fireWaitCnt; i++) { //等待烧掉的树遍历一遍,在这些等待烧的树里面选一个砍掉啊
		if (map[fireWaitList[i].x][fireWaitList[i].y] == 1) //出现一个1就变成0
			map[fireWaitList[i].x][fireWaitList[i].y] = 0;
		else
			continue;
		dfs(cutTreeNum - 1); //可以砍的树-1,继续砍树
		map[fireWaitList[i].x][fireWaitList[i].y] = 1; //回溯,把被砍掉的树还原成树木,for循环换下一棵树砍呗
	}
}

int  main()
{
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	treeCnt = 0;
	for (int ts = 0; ts < T; ts++) {
		scanf("%d %d", &N, &M);  //N*N的map,砍M棵树
		fireCnt = 0;  //初始化,火源的个数
		treeCnt = 0; //初始所有树的数量
		for (int i = 0; i < N; i++)
			for (int j = 0; j < N; j++) {
				scanf("%d", &map[i][j]); //矩阵
				if (2 == map[i][j]) {   //记录下火源的坐标和数量,fireCnt是火源的数量,(x,y)是火源的坐标
					fireList[fireCnt].x = i;
					fireList[fireCnt].y = j;
					fireCnt++;
				}
				else if (1 == map[i][j]) {  //treeCnt初始状态下所有树木的个数
					treeCnt++;
				}
			}
		fireWaitCnt = 0; //等待被烧的树的个数
		Answer = 0;
		Answer = getLeftTrees(1, 0); //烧完后剩下的树
		if (Answer != treeCnt) //烧完了后数量和最开始所有的1不一样,说明一定有树被烧了
		{
			dfs(M); //最多砍M棵树
		}
		printf("#%d %d\n", ts + 1, Answer);
	}
	return 0;
}
/*
input:
1
6 1
1 0 1 2 1 2
1 0 1 1 1 1
1 0 2 1 1 1
0 0 1 1 1 1
1 0 1 1 0 0
1 1 2 0 1 1
output:
#1 7
*/


6、basestation
//Base station ,蜂窝煤,4个相连的位置   (U1+U2+U3+U4)2求最大
//宽W,高H,就是有H行数据,奇数列不动,偶数列下沉一行,W>=3,H<=15,1<=U<=1000(每个蜂窝煤里面的人数)
//输入H、W,就是H行W列,然后偶数列下沉一行
//注意:是一行一行输入的,但是最后一个图的坐标给的是一列一列!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
int W, H;  //H*W的矩阵
int map[20][20];  //蜂窝煤的位置从1开始
bool flag[20][20];
int answer;
//6个方向可以走
//对于奇数列:上(x-1,y) 下(x+1,y) 左上(x-1,y-1) 右上(x-1,y+1) 左下(x,y-1) 右下(x,y+1)
//对于偶数列:上(x-1,y) 下(x+1,y) 左上(x,y-1) 右上(x,y+1) 左下(x+1,y-1) 右下(x+1,y+1)
int dir1[6][2] = { { -1, 0 },{ 1, 0 },{ 0, -1 },{ 0, 1 },{ -1, -1 },{ -1, 1 } };//奇数列
int dir2[6][2] = { { -1, 0 },{ 1, 0 },{ 0, -1 },{ 0, 1 },{ 1, -1 },{ 1, 1 } }; //偶数列下沉一列
extern void dfs(int x, int y, int user, int leftCells);
extern void _dfs(int x, int y, int user, int leftCells, int dir[6][2]);
int main()
{
	int T;
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++) {
		answer = -1;
		scanf("%d %d", &W, &H);  //H行,W列,这边input先给的列再给的行
		for (int i = 0; i < H; i++) {
			for (int j = 0; j < W; j++) {
				scanf("%d", &map[i][j]);
				flag[i][j] = false;  //初始化都是没走过的
			}
		}

		for (int i = 0; i < H; i++) {
			for (int j = 0; j < W; j++) {
				flag[i][j] = true;        //这个位置可以走
				dfs(i, j, map[i][j], 3);  //继续还剩3步没走
				flag[i][j] = false;   //回溯,退回来这一步不走了
			}
		}

		printf("#%d %d\n", tc, answer*answer);
	}
	system("pause");
	return 0;
}
void dfs(int x, int y, int user, int leftCells)//当前点的坐标,人数,剩下几个没走
{
	if (leftCells == 0) {  //剩余0个的时候说明走完了
		if (answer< user) {
			answer = user;
		}
		return;
	}

	if ((y + 1) % 2 == 0) {   //偶数列,坐标从0开始的,y+1才是列数
		_dfs(x, y, user, leftCells, dir2);
	}
	else {  //奇数列
		_dfs(x, y, user, leftCells, dir1);
	}
}

void _dfs(int x, int y, int user, int leftCells, int dir[6][2])
{
	for (int i = 0; i <6; i++) {    //6个方向可以走
		int nextX = x + dir[i][0];
		int nextY = y + dir[i][1];
		//新坐标没越界(从0开始) 且 没走过
		if (nextX >= 0 && nextX < H && nextY >= 0 && nextY < W && !flag[nextX][nextY]) {   
			flag[nextX][nextY] = true;     //走
			//当前和新坐标连在一起(有点一块区域那味儿了)
			//这两个dfs换个顺序结果一模一样的
			dfs(nextX, nextY, user + map[nextX][nextY], leftCells - 1);//可以往新坐标四周扩散走
			dfs(x, y, user + map[nextX][nextY], leftCells - 1);  //也可以往当前坐标的四周扩散走
			flag[nextX][nextY] = false;   //回溯,选择不走
		}
	}
}


7、轮胎压力测试
//轮胎压力测试
//损坏轮胎:胎压<0||胎压>K(0<K<=200)
//N个检测步骤,每一步先加压后减压,顺序可以调整
//使得轮胎正常通过测试的最小初始气压
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int T, N, K, in[8], out[8], answer;
bool flag[8];

void dfs(int currentPressure, int max, int min, int finished) {  //max是最大压,min是最小压
	if (answer <= 0 - min) {  //这不可能更小了,剪枝,这一步不走了
		return;
	}
	if (max - min > K) { //最大最小差大于K爆炸
		return;
	}
	if (finished == N) { //已经完成了N次又进来
		answer = 0 - min;  //得到了min是个负数,刚好让这个min大于0,就是最小初始气压
	}

	for (int i = 0; i < N; i++) {
		if (!flag[i]) {
			flag[i] = true; //用这一组来加压减压
			int first = currentPressure + in[i]; //加压后的状态
			int second = currentPressure + in[i] - out[i];  //加压再减压后的状态
			dfs(second, first > max ? first : max, second < min ? second : min, finished + 1);  //加压后跟max比取一个最大的,减压后跟min比取一个最小的
			flag[i] = false;//回溯,换一个试一下
		}
	}
}

int main(int argc, char** argv) {
	scanf("%d", &T);
	for (int test_case = 1; test_case <= T; ++test_case) {
		scanf("%d%d", &N, &K);
		for (int i = 0; i < N; i++) {
			scanf("%d", &in[i]);
		}
		for (int i = 0; i < N; i++) {
			scanf("%d", &out[i]);
			flag[i] = false;
		}

		answer = K;
		dfs(0, 0, 0, 0);
		if (answer == K) {
			answer = -1;
		}
		printf("#%d %d\n", test_case, answer);
	}
	system("pause");
	return 0;
}


8、Generating a map application
//Bus stops,有N*N的矩阵,其中有M个Stops,按顺序计算每个相邻stop之间最短路径有几条
//每一段的最短路径条数相乘,就是最多有多少条可以走的最短路径
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
int T, N, M, answer, matrix[20][20], stations[9][2];  //N*N的矩阵,有M个stops,stations存放M个stops的坐标
int d[4][2] = { { 0, 1 },{ 0, -1 },{ 1, 0 },{ -1, 0 } }, minDistance[9], minCount[9];  //minDistance放M个最短距离,minCount放每一段最短距离的个数
bool flag[9][20][20];  //M个stations在矩阵里面走没走过

void dfs(int station, int x, int y, int distance) {
	if (matrix[x][y] == 9) {  //当前这个点是障碍物
		return;
	}

	if (distance > minDistance[station]) {   //不是最短距离直接放弃
		return;
	}

	if (matrix[x][y] == station + 1) {  //找到了下一个station
		if (minDistance[station] > distance) //不停进来留下一个最短的
		{ 
			minDistance[station] = distance; //留下最短距离
			minCount[station] = 1;  //这是一条路径
		}
		else if (minDistance[station] == distance) {   //这就是最短,如果下次出现更短又被刷新成1了
			minCount[station]++;  //是下一条路径啊
		}
		return;
	}

	for (int i = 0; i < 4; i++) {   //4个方向走一遍
		int nextX = x + d[i][0];   //新坐标
		int nextY = y + d[i][1];  
		if (nextX >= 0 && nextX < N && nextY >= 0 && nextY < N && !flag[station][nextX][nextY]) {    //没越界并且没走过
			flag[station][nextX][nextY] = true;  // 选择走,继续下一个
			dfs(station, nextX, nextY, distance + 1);  
			flag[station][nextX][nextY] = false; //选择不走,回退,继续选择下一个方向走不走
		}
	}
}

int main(int argc, char** argv) {
	scanf("%d", &T);
	for (int test_case = 1; test_case <= T; ++test_case) {
		scanf("%d%d", &N, &M);  //N*N,M个stops
		for (int i = 1; i <= M; i++) {
			minDistance[i] = 999;  //初始化最短距离为最大值
			minCount[i] = -1;  //最多有多少个最短路径
		}
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				scanf("%d", &matrix[i][j]);
				if (matrix[i][j] != 0 && matrix[i][j] != 9) {  //非0和非9的时候必是站点,存放到stations数据
					stations[matrix[i][j]][0] = i;
					stations[matrix[i][j]][1] = j;
				}

				for (int k = 1; k <= M; k++) {//初始化都没走过
					flag[k][i][j] = false;
				}
				 每一步的起点直接标记,
				//flag[matrix[i][j]][i][j] = true;
			}
		}

		answer = 1;
		for (int station = 1; station <= M - 1; station++) { 
			dfs(station, stations[station][0], stations[station][1], 0); //从当前station开始找,输入当前station的坐标,当前步数0
			if (minCount[station] == -1) {
				answer = 0;
				break;
			}
			else {
				answer *= minCount[station];
			}
		}

		printf("#%d %d\n", test_case, answer);
	}
	system("pause");
	return 0;
}


9、黑白棋
//位置的数量N(N<=30)给出,初始状态给出,0空白1黑子2白字
//程序控制下黑棋,白棋每次固定放在所有空位的最左边的位置
//黑先白后,走3步
//求最后黑棋最多多少个
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
int T, N, init[30], answer;

void turnOver(int chess[], int position, int type) {
	int opposition = type == 1 ? 2 : 1;  //跟type取反
	int left = position;  //从left到right是需要翻转的区域
	int right = position;

	for (int i = position - 1; i >= 0; i--) {  //向左遍历翻转
		if (i == 0) {     //走到墙了,也就是最后一个
			if (chess[i] == 0) {  //这个如果是0,说明翻转不过来
				left = position;
			}
			else {  //不是0就是type或者opposition,如果是type,正好中间从1开始都得翻转成type,如果是opposition正好从0开始全部翻转成type
				left = 0;
			}
		}
		else {  //还没走到头的时候
			if (chess[i] == opposition) {  //如果这个位置是相反的,left不断跟着i移动
				left = i;
			}
			else if (chess[i] == type) {   //如果这个位置相同的,走到头了
				break;
			}
			else {   //chess[i]是0,到头了,不需要再走了,也不需要翻转
				left = position;
				break;
			}
		}
	}

	for (int i = position + 1; i < N; i++) {   //向右遍历翻转
		if (i == N - 1) {  //如果走到头了
			if (chess[i] == 0) {   //如果这个头还是空位0,那翻转不过来啊
				right = position;  
			}
			else {
				right = N - 1;  //否则这个头是type或者opposition一定翻转后到N-1都是type
			}
		}
		else {            //如果没走到头
			if (chess[i] == opposition){     //只要是相反的走继续跟着i移动
				right = i;
			}
			else if (chess[i] == type) {   //只要是相同的就停下
				break;
			}
			else {            //一直是相反的突然遇见空位0,翻转不过来
				right = position;
				break;
			}
		}
	}

	for (int i = left; i <= right; i++) {
		chess[i] = type;
	}
}

void dfs(int chess[], int steps) {
	if (steps == 3) {  //3步已经走完了
		int result = 0;
		for (int i = 0; i < N; i++) {  //计算当前这个状态下,黑子有多少个
			if (chess[i] == 1) {
				result++;
			}
		}
		if (answer < result) {  //出现更大的赋值啊
			answer = result;
		}
		return;
	}

	for (int i = 0; i < N; i++) {      //为什么黑棋不需要回溯,这个比较特殊,每次进来chess[i]的时候,chessCopy都重来到当前的状态再往下走,所以是6个位置都试了一遍的
		if (chess[i] == 0) //棋盘上出现一个空位
		{            
			int chessCopy[30];          
			for (int j = 0; j < N; j++) {         //chessCopy记录当前棋盘的状态
				chessCopy[j] = chess[j];
			}
			chessCopy[i] = 1;          //把黑棋放在这个空位上
			turnOver(chessCopy, i, 1);  //翻转

			for (int k = 0; k < N; k++) {   //然后放白棋
				if (chessCopy[k] == 0) {   // 找到第一个空位
					chessCopy[k] = 2;      //放白棋
					turnOver(chessCopy, k, 2);  //翻转
					break;
				}
			}
			dfs(chessCopy, steps + 1); //继续下一步
		}
	}
}

int main(int argc, char** argv) {
	scanf("%d", &T);
	for (int test_case = 1; test_case <= T; ++test_case) {
		scanf("%d", &N);
		for (int i = 0; i < N; i++) {
			scanf("%d", &init[i]); //棋盘的初始状态
		}

		answer = -1;
		dfs(init, 0);

		printf("#%d %d\n", test_case, answer);
	}
	system("pause");
	return 0;
}


10、气球射击(N最大是10,每个气球上的数字最大是100)
//气球射击,N个气球,N发子弹
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#define MAX 100
int N;//N个数字
int answer = 0;
int number[MAX], res[MAX], tmpres[MAX];
bool visit[MAX] = { 0 };
void solution(int num, int score)//跟我的对比,加标记回溯不需要复制整个map,更简洁
{
	if (num == N && answer < score) //已经射击了num个数字,并且答案比answer大
	{
		answer = score;
		for (int i = 0; i < N; i++)
		{
			res[i] = tmpres[i];
		}
		return;
	}

	for (int i = 0; i < N; i++) //N个数字遍历一遍
	{
		int left = -1, right = -1; //初始化左右
		if (visit[i] == false)  //没射击过数字number[i]
		{
			for (int j = i + 1; j < N; j++) //i这个数字的右边遍历一遍
			{
				if (visit[j] == false) // 一旦有一个没被射击的,那这个气球还在
				{
					right = j;
					break;
				}
			}
			for (int j = i - 1; j >= 0; j--)//i这个数字的左边遍历一遍
			{
				if (visit[j] == false) //一旦有一个没被射击的,那这个气球还在
				{
					left = j;
					break;
				}
			}

			visit[i] = true;  //把气球i射击了
			tmpres[num] = number[i];  //tmpres存放射击气球的顺序,当前第num次射击的是气球number[i]
			if (left >= 0 && right > 0) //如果左边有气球并且右边有气球
				solution(num + 1, score + number[left] * number[right]);
			else if (left >= 0 && right < 0) //左边有气球,右边没气球
				solution(num + 1, score + number[left]);
			else if (left < 0 && right > 0)  //左边没气球,右边有气球
				solution(num + 1, score + number[right]);
			else     //左右都没气球
				solution(num + 1, score + number[i]);
			visit[i] = false; //回溯,不射气球i,换下一个气球射击看看
		}
	}
}
int main()
{
	int tc;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (tc = 1; tc <= T; ++tc)
	{
		cin >> N;  //N个数字
		for (int i = 0; i <= N - 1; i++)
		{
			cin >> number[i];  
		}
		answer = 0;
		for (int i = 0; i < N; i++) //最开始都没被射击过
			visit[i] = 0;
		solution(0, 0);  // 最开始选0个,计算也是0
		cout << "#"<<tc<<" "<<answer << endl;
	}
	return 0;
}
/*
Input:
5
4
1 2 3 4
5
3 10 1 2 5
7
12 48 28 21 67 75 85
8
245 108 162 400 274 358 366 166
10
866 919 840 944 761 895 701 912 848 799

Output
20
100
16057
561630
6455522
*/


//我写的
#define _CRT_SECURE_NO_WARNINGS	
#include <stdio.h>
int N, init[400], answer;
int calc(int pos, int bal[], int len, int score)//选择位置pos射击,计算射击后的分数
{
	if (pos - 1 >= 0 && pos + 1<len)
	{
		score += bal[pos - 1] * bal[pos + 1];
	}
	if (pos - 1 >= 0 && pos == len - 1)
	{
		score += bal[pos - 1];
	}
	if (pos == 0 && pos + 1<len)
	{
		score += bal[pos + 1];
	}
	if (pos + 1 == len&&pos == 0)
	{
		score += bal[pos];
	}
	return score;
}
//当前的数组Line,当前得到的分数score,当前射击了step个气球,还剩下leave个气球没射击
void dfs(int Line[], int score, int step, int leave)
{
	if (step == N + 1)
	{
		if (score > answer)
		{
			answer = score;
		}
		return;
	}
	for (int pos = 0; pos <leave; pos++) //剩下leave个位置,选一个位置射击
	{
		int bal[400];
		for (int t = 0; t < N; t++)
		{
			bal[t] = Line[t];
		}
		int new_score = calc(pos, bal, leave, score);//射击pos位置
		//射击结束,数组改变,pos后面的往前移,数组长度leave-1
		for (int t = pos + 1; t<leave; t++)
		{
			bal[t - 1] = bal[t];
		}
		bal[leave - 1] = 0;
		//继续dfs
		dfs(bal, new_score, step + 1, leave - 1);
	}
}
int main()
{
	int test_case;
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (test_case = 1; test_case <= T; test_case++)
	{
		scanf("%d", &N);
		for (int i = 0; i < N; i++)
		{
			scanf("%d", &init[i]);
		}
		answer = 0;
		dfs(init, 0, 1, N);
		printf("#%d %d\n", test_case, answer);
	}

	return 0;
}

/*
Input:
5
4
1 2 3 4
5
3 10 1 2 5
7
12 48 28 21 67 75 85
8
245 108 162 400 274 358 366 166
10
866 919 840 944 761 895 701 912 848 799

Output
20
100
16057
561630
6455522

*/


11、构建城堡
//11构建城堡,1-9忍耐性,8个方向的空位>=忍耐度可被吹走,问需要多少天所有的城堡都不变
//思路:先遍历一遍,边界除外(边界一定全空),找到最开始第一时刻就能被吹走的沙子进队,
//然后队列中的沙子全变成空,继续BFS进队这些空位置旁边能被吹走的沙子
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define MAX 1001 //矩阵的边最大是1001
char map[MAX][MAX];
int H, W; //H*W矩阵
int Answer;
int head, tail;
int qx[MAX], qy[MAX], qstep[MAX];
int val[MAX][MAX];//每个城堡的8个方向能吹到风的有几个
int dir[8][2] = { { -1, 0 },{ -1, 1 },{ 0, 1 },{ 1, 1 },{ 1, 0 },{ 1, -1 },{ 0, -1 },{ -1, -1 } };
bool check(int x, int y)
{
	int ret = 0; //计算当前位置(x,y)周围的空方向有多少个,能不能被吹
	if (map[x][y] == '.') //当前位置是空白,不能吹
		return false;
	for (int i = 0; i < 8; i++) //8个方向试一遍,新方向如果是空白的,ret++
	{
		if (map[x + dir[i][0]][y + dir[i][1]] == '.') 
			ret++;
	}
	val[x][y] = ret; //当前位置周围空白的数量,初始计算了一遍啊
	if (ret >= map[x][y] - '0') //空白的数量>=持久度的时候,说明能够冲走
	{
		return true;//能冲走
	}
	return false; //不能冲
}
//第一次吹风被吹走的城堡出队
void compute()
{
	while (head < tail)
	{
		int currx = qx[head];
		int curry = qy[head];
		int currstep = qstep[head++]; //当前第几天吹
		for (int i = 0; i < 8; i++)
		{
			int xx = currx + dir[i][0];
			int yy = curry + dir[i][1];
			//空的海浪不用吹,9也不可能被海浪吹走,因为方向最多8
			if (map[xx][yy] == '.' || map[xx][yy] == '9')
				continue;

			val[xx][yy] += 1;//下一个点(xx,yy)会多一个从(x,y)吹过来的方向,因为(x,y)被沙吹走了
			if (val[xx][yy] >= (map[xx][yy] - '0')) //新的可以被吹走的沙子进队
			{
				qx[tail] = xx;
				qy[tail] = yy;
				qstep[tail++] = currstep + 1;

				map[xx][yy] = '.'; //吹走后变成"."
				Answer = currstep + 1;  //进一个天数+1
			}
		}

	}
}
int main()
{
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{    //H*W的矩阵
		scanf("%d %d", &H, &W);
		for (int i = 0; i <H; i++)
		{
			for (int j = 0; j <W; j++)
			{
				val[i][j] = 0;
			}
		}

		Answer = 0;
		tail = 0;
		head = 0;
		//char *str;
		for (int i = 0; i < H; i++)
		{
			scanf("%s", map[i]); //直接输入一行字符串,自动以\n终止,和下面等价
			/*for (int j = 0; j < W; j++)
			{
			char x;
			scanf("%c",&x);
			while (x==' '||x=='\n')
			{
			scanf("%c",&x);
			}
			map[i][j] = x;
			}*/
		}
		for (int i = 1; i < H - 1; i++) //所有的边界一定都是沙子,不需要计算了,全0
			for (int j = 1; j < W - 1; j++)
			{
				if (check(i, j) == true) //可以被冲走
				{
					//最开始能被冲走的进队,相当于找火源,这是海浪第一次来
					qx[tail] = i;
					qy[tail] = j;
					qstep[tail++] = 1;
				}
			}
		//队里所有的元素吹完变成'.'
		for (int i = 0; i < tail; ++i)
		{
			map[qx[i]][qy[i]] = '.';
		}
		compute();
		printf("#%d %d\n", tc, Answer);
	}
	return 0;
}
/*
input:
2

5 6
......
.939..
.3428.
.9393.
......

10 10
..........
.99999999.
.9.323239.
.91444449.
.91444449.
.91444449.
.91444449.
.91232329.
.99999999.
..........

output:
#1 3
#2 35

*/


12、给树木黑白染色
//Leetcode原题,就是二分图问题
//一个无向图有n个节点,其中每个节点都有一个介于0至n-1之间的唯一编号
//给一个二维数组,graph,A、B,每一条边的2个节点一个来自A,一个来自B
//是二分图返回true,否则false

//二分图,城市1和2,用一条边链接的一定不是同一分组的城市,输出城市编号1里面的城市数和城市编号
//给树木染色不需要输出城市数和编号,是二分图true,否false
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
using namespace std;
int v, e;
bool linked[1001][1001]={false};//城市编号从1开始
int group[1001]={0}, answer;
//和城市city相连且在同一分组no是矛盾的
bool check(int no, int city) { //判断在是否有2个城市相连却在同一分组的情况,一旦有就不是二分图
	for (int i = 1; i <= v; i++) {
		if (linked[i][city] && group[i] == no) {
			return true;
		}
	}
	return false;
}

bool dfs(int city, int no) {
	for (int j = 1; j <= v; j++) {  
	 //遍历所有跟城市i相连且未进行过分组的城市
		if (linked[city][j] && group[j] == 0) { 
			//想把j放到跟i相反的小组,但是必须保证j在这个小组没有相连的才能扔进去啊
			if (check(no == 1 ? 2 : 1, j)) { //j在的分组里面有没有跟j相连的
				return false;
			}
			else {
				group[j] = no == 1 ? 2 : 1;
			}
			//分完了j,继续分跟j相连的城市们
			if (!dfs(j, no == 1 ? 2 : 1)) {
				return false;
			}
		}
	}
	return true;
}
int main() {
	int tc;
	freopen("input.txt", "r", stdin);
	for (tc = 1; tc <= 10; tc++) {
		answer = 0;
		cin >> v >> e;
		for (int i = 0; i < e; i++) {
			int x, y;
			cin >> x >> y;
			linked[x][y] = true;
			linked[y][x] = true;
		}

		for (int i = 1; i <= v; i++) {
			if (group[i] == 0) {
				group[i] = 2;
				if (!dfs(i, 2)) {
					answer = -1;
					break;
				}
			}
		}
		//print
		if (answer != -1) {
			for (int i = 1; i <= v; i++) {
				if (group[i] == 1) {
					answer++;
				}
			}
		}
		cout << "#" << tc << " " << answer;
		if (answer > 0) {
			for (int i = 1; i <= v + 1; i++) {
				if (group[i] == 1) {
					cout << " " << i;
				}
			}
		}
		cout << endl;
	}

	return 0;
}
/*
输入:
6 7
2 1 1 4 2 3 5 2 2 6 4 3 6 4 
意思6个城市,7条边,2 1相连,2 4相连,2 3相连……
*/


13、four fundamental arithmetic operation四则运算
//补充答案:13、four fundamental arithmetic operation四则运算
//四则运算,输入是按照根左右,(左根右)计算表达式的值,输出整数,小数点后的不要了
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
char input[10];  //字符串最长是10
int n, node, left, right; //n个节点,node是节点编号
char Op[1010];   //存放每个编号的运算符
int Value[1010]; //存放每个编号的数
int Left[1010];  //存放左孩子的数
int Right[1010];  //存放右孩子的数
int get_number() //把字符串转换成数字
{
	int num = 0;
	for (int i = 0; i<10; i++)
	{
		if (input[i] == 0)break; //字符串以'\0'结尾,但是'\0'的assci和0一样,所以随便用一个来判断等价
		num *= 10;
		num += input[i] - '0';
	}
	return num;
}
double dfs(int root)
{
	if (Left[root] == 0 && Right[root] == 0)return Value[root];  //左右孩子都是空的
	double l = dfs(Left[root]);  //左右根啊
	double r = dfs(Right[root]);
	if (Op[root] == '+')return l + r;
	if (Op[root] == '-')return l - r;
	if (Op[root] == '*')return l * r;
	if (Op[root] == '/')return l / r;
}
int main()
{
	int test_case;
	freopen("input.txt", "r", stdin);
	int T=10;
	for (test_case = 1; test_case <= T; ++test_case)
	{
		scanf("%d", &n);         //一棵树有n个节点
		for (int i = 0; i <= n; i++)Left[i] = Right[i] = 0;//初始化n个节点的左子树和右子树都是0
		for (int i = 0; i<n; i++)
		{
			scanf("%d", &node);   //输入节点编号
			scanf("%s", input);   //输入一个字符串,如果是运算符一定有左右孩子,是数字一定没孩子
			if (input[0] >= '0' && input[0] <= '9')Value[node] = get_number();  //是数字,输入这个数字的值
			else   //是运算符
			{
				Op[node] = input[0];  //这个运算符保留下来
				scanf("%d", &left);  //读取左右孩子
				scanf("%d", &right);
				Left[node] = left;
				Right[node] = right;
			}
		}
		printf("#%d %d\n", test_case, (int)dfs(1));
	}
	return 0;
}
/*
input:
2 ← 2个case

5 ← TestCase 1.
1 - 2 3  ← 编号1的符号是“-”,左孩子是编号2,右孩子是编号3
2 - 4 5  ← 编号2的符号是“-”,左孩子是编号4,右孩子是编号5
3 10     ← 编号3的值是10
4 88     ← 编号4的值是88
5 65     ← 编号3的值是65

7  ← TestCase 2.
1 / 2 3    
2 - 4 5
3 - 6 7
4 261
5 61
6 81
7 71

output:
#1 13
#2 20

*/


14、Good taxi driver
//好司机,给N个节点,从节点0以70的速度出发,到目标点D,每一条路是单向的,给出速度限制、距离,
//限制时间v==0时,按照上一个节点的速度行驶,不为0则按照最大速度v行驶,求从节点0到D的最短时间和路径
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int N, M, D, num;
double answer;
struct road
{
	int city;
	int V;
	int L;
};
road map[150][150];  //map[i][0],map[i][1]……是指城市i指向的城市有0、1
/*
等价于
struct road
{
	int cnt;//出度,城市指向的城市个数
	int city[150];
	int V[150];
	int L[150];
};
road map[150]
*/
int roadnum[150] = { 0 }; //存放节点i的出度的个数
int route[150], tmpr[150]; //route存放最短时间那条路径,tmpr存放每条路径
int visit[150]; //避免死循环
//当前在城市city,当前走sum个城市了(需要打印最短路径),当前花费的时间time,
//当前的速度是v(需要标记速度是因为如果限速V=0,需要取前一个的速度)
void dfs(int city, int sum, double time, int v) //dfs(0, 0, 0, 70);
{
	tmpr[sum] = city; //第num+1个选当前这个city,用num是因为数组编号0开始的
	if (time >= answer)
		return;
	if (city == D)//走到终点了
	{
		if (time<answer)
		{
			answer = time;
			for (int i = 0; i <= sum; i++)
			{
				route[i] = tmpr[i];
			}
			num = sum;
		}
		return;
	}

	visit[city] = 1;//走这条路
	for (int i = 0; i < roadnum[city]; i++) //这个城市指向的城市有roadnum[city]个
	{
		road cur = map[city][i];  // cur存放当前city指向的城市们、城市们的速度限制、距离
		if (visit[cur.city] == 0) //没走过
		{
			double speed = (cur.V == 0) ? v : cur.V; //限速0就取当前速度,否则就取限制速度
			double curtime = time + cur.L / speed;
			dfs(cur.city, sum + 1, curtime, speed);
		}
	}
	visit[city] = 0;//回溯,不走这条路
}
int main()
{
	int tc;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (tc = 1; tc <= T; tc++)
	{

		answer = 9999; //求最短时间,初始化为最大
		cin >> N >> M >> D; //N个城市,M条路,目的地是D
		int a, b;
		for (int i = 0; i < N; i++) //存放每个城市出度的个数
			roadnum[i] = 0;
		for (int i = 0; i < M; i++)   //M条路
		{
			cin >> a >> b;  //a到b
			map[a][roadnum[a]].city = b;   //map[a]存放的是a指向的城市们
			cin >> map[a][roadnum[a]].V >> map[a][roadnum[a]].L; //指向的城市的速度限制和距离
			roadnum[a]++;
		}
		dfs(0, 0, 0, 70);
		cout << '#' << tc;
		for (int i = 0; i <= num; i++)
		{
			cout << ' ' << route[i];
		}
		cout << endl;
	}
	return 0;
}
/*
input:
2

4 4 3
0 1 1 70
0 2 0 70
1 3 0 70
2 3 0 40


6 15 1
0 1 25 68
0 2 30 50
0 5 0 101
1 2 70 77
1 3 35 42
2 0 0 22
2 1 40 86
2 3 0 23
2 4 45 40
3 1 64 14
3 5 0 23
4 1 95 8
5 1 0 84
5 2 90 64
5 3 36 40

output:
#1  0 2 3
#2  0 5 2 3 1
*/



15、Hamburger Master做汉堡
//Hamburger Master
//N种原材料,M组不能在一起
//答案比我的简洁,都是对的
//做汉堡,给N种原材料,编号从1到N,给M组数据,每一组的2个数字代表不能同时出现
//有多少种组合方式?
//空集合、选一个、选2个
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int N, M, kind = 0, num;//限制的组合原料个数
int visit[21] = { 0 };
int map[21][21] = { 0 };
void dfs(int n, int nn) //当前已经选了n个原材料,当前选的原材料是nn
{
	if (n == num) //当前选的原材料个数和目标原材料个数一样,是一种方案
	{
		kind++;
		return;
	}
	if (N - nn<num - n) //剩下的原材料个数<剩下需要选的个数,剩下全选也不够,剪枝
		return;
	for (int i = nn + 1; i <= N; i++) //当前选择的原材料编号往后开始尝试
	{

		if (visit[i] == 0 && map[nn][i] == 0) //如果没选过并且是可以跟当前选的nn在一组的
		{
			// if (N - nn<num - n) //剩下的原材料个数<剩下需要选的个数,剩下全选也不够,剪枝
				// return;
			visit[i] = 1;  //选i
			dfs(n + 1, i);
			visit[i] = 0; //不选原材料i
		}
	}
}
int main(int argc, char **argv)
{
	int test_case;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (test_case = 1; test_case <= T; ++test_case)
	{
		cin >> N >> M;
		for (int i = 0; i < M; i++)
		{
			int a, b;
			cin >> a >> b;
			map[a][b] = 1;//不能在一组的赋值1
			map[b][a] = 1;
		}
		for (int i = 0; i <= N; i++) //选择0个、1个……N个原材料进行组合
		{
			num = i; //num是原材料全局变量
			dfs(0, 0); //最开始一个没选,选的原材料是0(就是没选,编号从1开始)
		}
		//清空visit和map(map必须清空)
		for (int i = 1; i <= N; i++)
		{
		    visit[i] = 0;//注释掉结果一样,因为是回溯,所以全部结束之后本身就变全0
			for (int j = 1; j <= N; j++)
				map[i][j] = 0;
		}
		cout << '#' << test_case << " " << kind << endl;
		kind = 0;
	}
	return 0;
}

/*
input
4

3 2
1 2
2 3

3 0

3 3
1 2
1 3
2 3

5 2
1 2
1 3

output
#1 5
#2 8
#3 4
#4 20
*/


//我写的
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, M, answer, group[400][2];
int select[20] = { 0 };
struct MyStruct
{
	int cnt;//不能在一组的有几个
	int next[20];//不能在一起的是哪些
}ingredient[21];
bool IsSelect(int t, int num)//已经抽了num个,t能不能抽
{
	for (int i = 0; i < num; i++)
	{
		if (select[i] == t) return false; //抽过的数字不能再抽
		for (int j = 0; j < ingredient[select[i]].cnt; j++)
		{
			if (t == ingredient[select[i]].next[j])
			{
				return false;
			}
		}
	}
	return true;
}
void dfs(int node, int deep, int x)//抽x个,当前开始抽第deep个,从当前node往后尝试
{
	if (x >= N) return; //x最多只能是N
	if (deep == x)//抽完了
	{
		///*抽过的select数组不能再计算一次
		//printf("\n%d\t", x);
		//for (int i = 0; i < 20; i++)
		//{
		//	printf("%d ", select[i]);
		//}*/
		answer++;
		return;
	}
	for (int i = node + 1; i <= N; i++)
	{
		if (IsSelect(i, deep)) //当前选了deep个,i能不能抽
		{
			select[deep] = i; //选择i,继续dfs
			dfs(i, deep + 1, x);
		}
	}
}
int main()
{
	freopen("input.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{
		scanf("%d%d", &N, &M);//N种材料,M组不可以一起选择
		for (int i = 1; i <= N; i++)
		{
			ingredient[i].cnt = 0;
		}
		for (int i = 0; i < M; i++)
		{
			int t1, t2;
			scanf("%d %d", &t1, &t2);
			ingredient[t1].next[ingredient[t1].cnt] = t2;
			ingredient[t1].cnt++;
			ingredient[t2].next[ingredient[t2].cnt] = t1;
			ingredient[t2].cnt++;
		}
		//N>=1
		if (N == 1)
		{
			answer = 2;//不选或全选
		}
		else  //N>1
		{
			if (M == 0) answer = 1 + N + 1;//一个都不选,每个单独选,全选
			else answer = 1 + N; //一个都不选,每个单独选
			for (int t = 2; t < N; t++) //从抽取2个到N-1个
			{
				//select置空
				for (int i = 0; i < 20; i++)
				{
					select[i] = 0;
				}
				dfs(0, 0, t);
			}
		}
		printf("#%d %d\n", tc, answer);
	}
	return 0;
}


16、Paul's Trip to Jdju Island 
//16吉普岛旅游,位置N,天数M,只有旅游地点是走过不能再走的
//可以参观N个地点,包括机场、酒店和旅游点,旅游点有满意度和旅游时间
//每一天必须9h内回酒店(随便一个酒店都行),机场只有1个
//机场(A)、旅游点(P)、酒店(H),求最大满意度,打印路径
#define _CRT_SECURE_NO_WARNINGS   
#include<iostream>
using namespace std;
struct Data //不能用data,因为data是namespace里面已有的变量名
{
	int type; //类型:是airport还是hotel还是tourist
	int time; //停留时间
	int level; //满意度
};
int N, M; //N个位置,M天
int V[36][36];
Data pos[36];
int start;
int visit[36]; //标记走没走过
int route[36], routeN; //存放满意度最高的路径
int rr[36], rrN; //存放每一条路径
int ans;
//find(start, 0, 0, 0);
//当前在位置n,是第day天,花费了time时间,当前满意度sat
void find(int n, int day, int time, int sat)
{
	if (day == M) //到第M天了
	{
		if (sat > ans)
		{
			ans = sat;
			routeN = rrN;  //rrN是当前的路径经过的位置个数
			for (int i = 0; i < rrN; i++) //route是满意度最高的路径
				route[i] = rr[i];
		}
		return;
	}
	for (int i = 1; i <= N; i++) //N个位置遍历一遍
	{
		//最后一天在机场的时候停止
		//如果这个位置是机场,并且当前是最后一天(停止)或者走过了
		if ((i == start && day != M - 1) || visit[i] == 1)
			continue;
		//一天的时间不能超过9小时,也就是540分钟
		//当位置i是旅游地,并且当前的时间足够过去并且参观
		//只有旅游地是不能反复走的,同一个hotel可以住好几次
		if (pos[i].type == 2 && time + V[n][i] + pos[i].time <= 540)
		{
			visit[i] = 1; //去这个地方旅游
			rr[rrN++] = i; //放进当前这个数组里面
			find(i, day, time + V[n][i] + pos[i].time, pos[i].level + sat); //继续找下一个旅游地
			rrN--; //回溯,不选这个旅游地旅游
			visit[i] = 0;
		}
		//如果现在是最后一天了,当前位置n到飞机场i的时间是足够的
		else if (day == M - 1 && pos[i].type == 1 && time + V[n][i] <= 540)2

4 4 3
0 1 1 70
0 2 0 70
1 3 0 70
2 3 0 40


6 15 1
0 1 25 68
0 2 30 50
0 5 0 101
1 2 70 77
1 3 35 42
2 0 0 22
2 1 40 86
2 3 0 23
2 4 45 40
3 1 64 14
3 5 0 23
4 1 95 8
5 1 0 84
5 2 90 64
5 3 36 40

		{
			rr[rrN++] = i; //去飞机场
			find(i, day + 1, 0, sat); //继续走最后一天
			rrN--; //也可以不去飞机场
		}
		//现在不是最后一天,位置i是hotel,并且时间足够我从当前位置n到i
		//9小时内必须回到酒店,同一家还是另外一家无关紧要
		else if (day != M - 1 && pos[i].type == 3 && time + V[n][i] <= 540)
		{
			rr[rrN++] = i; //放进路径数组
			find(i, day + 1, 0, sat); //继续走下一天
			rrN--; //回溯,不是最后一天,可以去酒店但是我不去,我去下一个酒店看看
				   //如果不去这个酒店,最后可能会出现时间不够,不能进入下一天的可能,这正好断掉没递归了啊
		}

	}
}
int main(int argc, char** argv)
{
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (int tc = 1; tc <= T; tc++)
	{
		routeN = rrN = 0;//初始化
		ans = 0;
		cin >> N >> M; //N个位置,M天
					   //maxTime = M * 24;
		for (int i = 1; i <= N - 1; i++) //位置1、2、3、……N-1分别到(2、3……N)、……(N)的时间
		{
			for (int j = i + 1; j <= N; j++)
			{
				cin >> V[i][j]; //无向图,反向时间也是一样的
				V[j][i] = V[i][j];
			}
		}

		for (int i = 1; i <= N; i++) //看N个位置是啥啊
		{
			visit[i] = 0; //初始化,最开始都是没走过的
			char c;
			cin >> c;
			if (c == 'A') //A说明是airport
			{
				pos[i].type = 1;
				start = i;  //airport是起点
				pos[i].time = 0;
				pos[i].level = 0;
			}
			else if (c == 'P') //P是旅游地点
			{
				int a, b;
				cin >> a >> b; //旅游地点的时间和满意度
				pos[i].type = 2;
				pos[i].time = a;
				pos[i].level = b;
			}
			else if (c == 'H') //H是酒店
			{
				pos[i].type = 3;
				pos[i].time = 0;
				pos[i].level = 0;
			}

		}

		find(start, 0, 0, 0);

		cout << '#' << tc << ' ' << ans;
		for (int i = 0; i < routeN; i++)
		{
			cout << ' ' << route[i];
		}
		cout << endl;
	}
	return 0;
}

/*
input:
第一行:T
第二行:N(位置的个数,包括airport、hotel、tourist),M(天数,一天9小时)
第三行:位置1到2、3、4……N的时间,单位mins
第四行:位置2到3、4、5……N的时间,单位mins
……
第N-1+ 2行:位置N-1到N的时间,单位mins
下面是
A:airport
P:每个tourist参观时间、满意度
H:hotel


1
10 3
60 90 100 110 240 40 30 60 30
60 120 140 200 120 100 60 60
90 110 170 100 100 30 90
50 110 40 80 90 90
70 30 50 90 90
100 140 180 120
40 90 40
100 20
70
A
P 240 6
P 120 4
P 240 5
P 60 4
P 200 6
P 180 1
P 180 1
H
H

Output Example
#1 25 2 3 9 5 6 10 4 1	// Output of 1st TC

*/


17、Planet Mars
//正长方体长宽高分别为n、m、k,由n*m*k个1*1*1的小立方体组成,把这些小立方体编号为0-(n*m*k-1),再给l个编号,表示这些小立方体是存在的,否则就是不存在的。求最总整个图形的外表面积
//解题思路:以其中一个存在的小立方体开始,上下左右前后6个方向搜索,如果有小方块就转移到这个小方块上继续搜索,没有则表面积+1,但是这个方法求出来的包含内表面积
//反向思维,这个立方体的周围包一层不存在的立方体(map[i][j][k]=0,存在为1),然后再按上面的方法改成搜索不存在的小立方体,只搜索最外圈。
//得到的结果是最外圈那些不存在的立方体的内外表面积,内表面积是答案(就是存在的立方体的外表面积),外表面积是(n*m+n*k+m*k)*2不需要计算,不需要算,搜索的时候如果是边界不+1就行
//搜索 + 洪泛算法,求外表面积
#define _CRT_SECURE_NO_WARNINGS   
#include <stdio.h>
#define MAX 61
//(1 <= n, m, k <= 60)
int N, M, K, L; //N*M*K的正长方体
int map[MAX][MAX][MAX];
int visit[MAX][MAX][MAX];
int sum; //表面积
void DFS(int n, int m, int k);
int main()
{
	freopen("input.txt", "r", stdin);
	int tc;
	scanf("%d", &tc);
	for (int t = 1; t <= tc; t++)
	{
		//n m k l
		scanf("%d", &N);
		scanf("%d", &M);
		scanf("%d", &K);
		scanf("%d", &L);

		sum = 0;
		//从后面输入可以看出,编号0的坐标是(1,0,0),所以输入占用的是[1,N],涂层外面一圈就是变成[0,N+1]
		for (int i = 0; i <= N + 1; i++) 
			for (int j = 0; j <= M + 1; j++)
				for (int a = 0; a <= K + 1; a++)
				{
					map[i][j][a] = 0;
					visit[i][j][a] = 0;
				}
		for (int i = 0; i < L; i++) //L个ACM的网格位置
		{
			int temp;
			scanf("%d", &temp);//acm小方块的编号
			//横坐标是temp%N+1,纵坐标是(temp/N)%M+1,竖坐标是(M*N)+1
			//编号temp转换成坐标为(temp%N+1,(temp/N)%M+1,(M*N)+1)
			map[temp%N + 1][(temp / N) % M + 1][temp / (M*N) + 1] = 1;
		}
		DFS(0, 0, 0);//这个坐标是被涂上去的一层,是空白的,从随便一个空白小方块开始dfs
		printf("#%d %d\n", t, sum);
	}
	return 0;
}

//从空白小方块开始dfs,如果是实心小方块,sum++,如果是空白小方块,跳到空白小方块上去
void DFS(int n, int m, int k) //上下左右前后6个方向
{
	visit[n][m][k] = 1;

	if (n-1>=0) //上
	{
		if (map[n - 1][m][k]) sum++; //发现是acm,那就sum++
		else if (!visit[n - 1][m][k]) //否则是空的,跳到这个空的小方块
			DFS(n - 1, m, k);
	}

	if (n+1<= N+1) //下,边界被扩展成了N+1
	{
		if (map[n + 1][m][k]) sum++;
		else if (!visit[n + 1][m][k])
			DFS(n + 1, m, k);
	}

	if (m-1>=0) //左
	{
		if (map[n][m - 1][k]) sum++;
		else if (!visit[n][m - 1][k])
			DFS(n, m - 1, k);
	}

	if (m+1<= M+1) //右,边界被扩展成了M+1
	{
		if (map[n][m + 1][k]) sum++;
		else if (!visit[n][m + 1][k])
			DFS(n, m + 1, k);
	}

	if (k-1>=0) //前
	{
		if (map[n][m][k - 1]) sum++;
		else if (!visit[n][m][k - 1])
			DFS(n, m, k - 1);
	}

	if (k+1<= K+1)  //后,边界被扩展成了K+1
	{
		if (map[n][m][k + 1]) sum++;
		else if (!visit[n][m][k + 1])
			DFS(n, m, k + 1);
	}
}

/*
Sample Input
2
2 2 1 3  //2*2*1的立方体,3个acm,3个acm的编号
0 1 3

3 3 3 26  //3*3*3的立方体,26个acm的编号
0 1 2 3 4 5 6 7 8 9
10 11 12 14 15 16 17 18 19 20
21 22 23 24 25 26
Sample Output
#1 14
#2 54

*/


18、闯关游戏
//闯关游戏,过关、雇佣或打架,最少需要多少钱能够通过所有关卡
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, P[9999], $[9999], answer;
extern void check(int mission, int money, int soldier3, int soldier2, int soldier1);
int main() {
	int T;
	scanf("%d", &T);
	for (int test_case = 1; test_case <= T; test_case++)
	{
		scanf("%d", &N);
		for (int i = 0; i < N; i++) {
			scanf("%d %d", &P[i], &$[i]);
		}
		answer = 0x7FFFFFFF;
		//第一关可以选择pass或者雇佣,不能fight
		check(1, $[0], 0, 0, 0);//第一关pass
		check(1, $[0] * 2, P[0], 0, 0); //第一关hire
		printf("#%d %d\n", test_case, answer);
	}
	return 0;
}
//第mission关,已经花费money的钱,还能打架3次、2次和1次的怪兽
void check(int mission, int money, int soldier3, int soldier2, int soldier1) {
	if (money >= answer)
	{
		return;
	}
	if (mission == N && money < answer)
	{
		answer = money;
		return;
	}
	// pass
	check(mission + 1, money + $[mission], soldier3, soldier2, soldier1);
	// 下一关不是最后一关的时候hire,如果是最后一关没必要hire了
	if (mission + 1 != N)
	{
		check(mission + 1, money + $[mission] * 2, soldier3 + P[mission], soldier2, soldier1);
	}
	// battle
	int allSoldier = soldier3 + soldier2 + soldier1;
	if (allSoldier >= P[mission])//总人数大于这一关怪兽数才能fight
	{
		//1够不够打架,不够2参与打架
		int left2 = soldier2 + soldier1 - P[mission] < soldier2 ? soldier2 + soldier1 - P[mission] : soldier2;
		if (left2 < 0)//负数说明3也要参与打架
		{
			left2 = 0;
		}
		//1和2够不够打架,不够3参与打架
		int left3 = allSoldier - P[mission] < soldier3 ? allSoldier - P[mission] : soldier3;
		check(mission + 1, money, 0, left3, left2);
	}
}


19、训练士兵ThreeKingDoms-Chapter1
//我写的不对,食物用完了不能作为终止,食物完了前面选的还在increase
//ThreeKingDoms-Chapter1
//N个月,M个食物,K种军队,每种军队有cost(food)、base power、increase power,求M够用时训练得到最大的power
//power的计算方法是第i天选的军队,增加了N-i次power,最后算总和啊,存在还没到最后一天就没有粮食了的情况啊
#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
struct armyinfo {
	int cost; //需要的食物
	int base; //基础的能量
	int increase; //增值的能量
};
int M, N, K, answer, mark;//mark标记那些没到最后一天但是没食物的了,不然进不来mon==N,dfs结束不了
armyinfo army[10];
int visit[10] = { 0 };
void dfs(int m, int total, int mon) { //食物还剩m,总共的power是total,当前是第mon天
	if (mon == N) { //N天结束了
		if (total>answer)
			answer = total;
		return;
	}
	mark = 0;//每次进来mark=0,看有没有选下一个啊(选了会直接变成1),选不了直接结束
	for (int i = 0; i < K; i++) {  //遍历K种军队
		if (m - army[i].cost >= 0 && visit[i] == 0) {  //食物够用并且没走过
			mark = 1;
			visit[i] = 1;  //选,也能不选回溯
			dfs(m - army[i].cost, total + (N - mon - 1)*army[i].increase + army[i].base, mon + 1);
			visit[i] = 0;
		}
	}
	//case1:如果前面的食物已经不够选别的军队,但是又没到最后一天
	if (mark == 0) {  
		dfs(m, total, N);
	}
}
int main()
{
	int tc;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (tc = 1; tc <= T; ++tc)
	{
		answer = 0;
		cin >> M >> N >> K;  //M个食物,N天,K种军队
		for (int i = 0; i < K; i++) {
			cin >> army[i].cost >> army[i].base >> army[i].increase;
		}
		dfs(M, 0, 0);
		cout << '#' << tc << ' ' << answer << endl;
	}
	return 0;
}


20、骨头的诱惑
//骨头的诱惑
//N*M的长方形迷宫在T时刻会打开一个门,只开1s,小狗必须恰好在第T秒开启
//每秒钟小狗可以上下左右移动到相邻的放各种,这个方格开始下沉,1s后消失,所以一个方格不能停留超过1s,也不能回到经过的方格
//小狗能否顺利逃脱?可以输出yes,否则输出no
//N*M(N>1,M<7)的矩阵T(T的范围(0,50))时刻开门
//X:墙壁,S:小狗所处的位置,D:迷宫的门,.:空的方格
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, M, T,startX,startY,endX,endY,answer;
int dir[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };
int grid[200][200];
void dfs(int x,int y,int step)
{
	grid[x][y] = 0;
	if (step == T&&x == endX&&y == endY)
	{
		answer = 1;
		return;
	}
	for (int i = 0; i < 4; i++)
	{
		int xNext = x + dir[i][0];
		int yNext = y + dir[i][1];
		//没选过且没越界
		if (grid[xNext][yNext] == 1 && xNext >= 0 && xNext < N&&yNext>=0&&yNext<M)
		{
			grid[xNext][yNext] = 0;//回溯
			dfs(xNext, yNext, step + 1);
			grid[xNext][yNext] = 1;//回溯
		}
	}
}
int main() 
{
	int t;
	freopen("input.txt", "r", stdin);
	scanf("%d", &t);
	for (int tc = 1; tc <= t;tc++)
	{
		scanf("%d %d %d",&N,&M,&T);//N*M的矩阵T时刻开门
		//一行读一个字符串好了
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				char x,y;
			    scanf("%c", &x); //scanf("%s", &x);
				while(x == ' ' || x == '\n')
				{
					scanf("%c", &y);
					x = y;
				}
				printf("%c",x);
				if (x == 'S')
				{
					startX = i;
					startY = j;
					grid[i][j] = 1;
				}
				if (x == 'D')
				{
					endX = i;
					endY = j;
					grid[i][j] = 1;
				}
				if (x == '.')
				{
					grid[i][j] = 1;
				}
				if (x == 'X')
				{
					grid[i][j] = 0;
				}
			}
		}
		answer = 0;
		dfs(startX, startY, 0);
		if (answer == 0)
		{
			printf("#%d NO\n", tc);
		}
		else
		{
			printf("#%d YES\n", tc);
		}
		
	}
	return 0;
}

/*
input:
3

3 4 5
S . . .
. X . X
. . . D

4 4 8
. X . X
. . S .
. . . .
D X . X

4 4 5
S . X .
. . X .
. . X D
. . . .


output:
#1 YES
#2 YES
#3 NO

*/


21、激光(方块黑白棋啊)
①暴力
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int M, N, K;//M*N,K
int answer;
int map[20][100];
int calc()
{
	int res = 0, j;
	for (int i = 0; i< M; i++)
	{
		for (j = 0; j < N; j++)
		{
			if (map[i][j] == 0) break;
		}
		if (j == N) res++;
	}
	if (res == 0) return -1;
	else return res;
}
void dfs(int line, int k)
{
	//到最后一列并且剩下偶数次,结束,到k==0结束
	if (line > N || k<0)return;
	if (k == 0 || (line == N&&k % 2 == 0))
	{
		int curr_answer = calc();
		if (curr_answer>answer)
		{
			answer = curr_answer;
		}
		return;
	}
	//翻转line这列,M*N
	for (int i = 0; i < M; i++)
	{
		map[i][line] = 1 - map[i][line];
	}
	dfs(line + 1, k - 1);
	//不翻转
	for (int i = 0; i < M; i++)
	{
		map[i][line] = 1 - map[i][line];
	}
	dfs(line + 1, k);
}

int main()
{
	freopen("input.txt", "r", stdin);
	int t;
	scanf("%d", &t);
	for (int tc = 1; tc <= t; tc++)
	{
		scanf("%d%d%d", &M, &N, &K);
		for (int i = 0; i < M; i++)
		{
			for (int j = 0; j < N; j++)
			{
				scanf("%d", &map[i][j]);
			}
		}
		answer = calc();
		dfs(0, K);
		printf("#%d %d\n", tc, answer);
	}
	return 0;
}
②行结构(位运算就算了)
//黑白棋,找最有潜力的一种行
/*用C++写成string会更简单一些
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;//用它说明是C++
int main()
{
string x;
int t;
cin >> t;
for (int tc = 1; tc <= t; tc++)
{
int num;
cin >> num;
x += '0' + num;
}
cout << x[0] << x[1] << x[2] << x[3] << x[4];
return 0;
}
*/

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int m, n, K;//m*n,k
int answer;
int map[100];
struct MyStruct
{
	int num;
	int cnt;
};
int count;//多少种类的行
MyStruct new_map[100]; //每种行的结构和数量

bool isInNew(int number)
{
	for (int i = 0; i < count; i++)
	{
		if (new_map[i].num == number)
		{
			new_map[i].cnt++;
			return true;
		}
	}
	return false;
}
bool isOver(int number)
{
	int need = 0;//0的个数
	while (number > 0) //1的个数
	{
		if (number % 2 == 1)need++;
		number /= 2;
	}
	need = n - need;
	if (need == K) return true;
	if (need < K && (K - need) % 2 == 0) return true;
	return false;
}
int main()
{
	freopen("input.txt", "r", stdin);
	int t, x;
	scanf("%d", &t);
	for (int tc = 1; tc <= t; tc++)
	{
		scanf("%d%d%d", &m, &n, &K);
		for (int i = 0; i < m; i++)  //m*n
		{
			new_map[i].cnt = 0;
		}
		count = 0, answer = -1;
		for (int i = 0; i < m; i++)  //m*n
		{
			map[i] = 0;
			for (int j = 0; j < n; j++)
			{
				scanf("%d", &x);
				map[i] <<= 1;
				map[i] |= x;
			}
			if (isInNew(map[i]) == false)
			{
				new_map[count].num = map[i];
				new_map[count++].cnt = 1;
			}
			//printf("map[%d]:%d ",i, map[i]);	
		}
		//寻找数量最多的那一类吧?不行就换下一个,冒泡增加时间复杂度了
		//排序后从数量最大的那种行结构开始尝试
		for (int i = 0; i < count; i++)
		{
			if (isOver(new_map[i].num))
			{
				if (answer<new_map[i].cnt)answer = new_map[i].cnt;
			}
		}
		printf("#%d %d\n", tc, answer);
	}
	return 0;
}



22、水管工游戏
1号:上右,2号:下右,3号:下左,4号:上左; 5号:左右,6号:上下
①申立明版本
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int g[15][15];//存放这个map
int vis[15][15]; //标记数组
int n, m; //n*m的矩阵

int dr[4] = { -1, 0, 1, 0 }; //方向:上右下左
int dc[4] = { 0, 1, 0, -1 };

int path[100][2]; //打印某一条是通的路径
int idx; //路径里面坐标的个数

bool solve(int r, int c, int d) //在坐标(r,c),水流从d方向来的
{
	if (r == n && c == m)//当前的点是终点的话
	{
		if (g[r][c] > 4 && d == 1)return true; //如果是类型5、6类型的水管,当前位置必须从1(左边)进水
		if (g[r][c] <= 4 && d == 2)return true;//如果是其他类型的水管,当前位置必须从2(上边)进水
	}

	if (g[r][c] > 4) //如果当前水管是5、6类型
	{
		int nr = r + dr[d];  //下一个坐标的流过来的水流方向不变啊
		int nc = c + dc[d];
		if (nr > 0 && nr <= n && nc > 0 && nc <= m && !vis[nr][nc] && g[nr][nc] != 0)
		{
			path[idx][0] = nr; path[idx++][1] = nc;
			vis[nr][nc] = 1;
			if (solve(nr, nc, d))return true; //d不变
			vis[nr][nc] = 0;
			idx--;
		}
	}
	else
	{
		int nd = (d + 3) % 4;
		int nr = r + dr[nd];
		int nc = c + dc[nd];
		if (nr > 0 && nr <= n && nc > 0 && nc <= m&& !vis[nr][nc] && g[nr][nc] != 0)
		{
			path[idx][0] = nr; path[idx++][1] = nc;
			vis[nr][nc] = 1;
			if (solve(nr, nc, nd))return true;
			vis[nr][nc] = 0;
			idx--;
		}

		nd = (d + 1) % 4;
		nr = r + dr[nd];
		nc = c + dc[nd];
		if (nr > 0 && nr <= n && nc > 0 && nc <= m && !vis[nr][nc] && g[nr][nc] != 0)
		{
			path[idx][0] = nr; path[idx++][1] = nc;
			vis[nr][nc] = 1;
			if (solve(nr, nc, nd))return true;
			vis[nr][nc] = 0;
			idx--;
		}
	}
	return false;
}

int main()
{
	freopen("input.txt", "r", stdin);

	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) //n*m的矩阵,坐标从1开始
		for (int j = 1; j <= m; j++)
			scanf("%d", &g[i][j]);

	path[idx][0] = 1; path[idx++][1] = 1;  //起始坐标(1,1)进去
	vis[1][1] = 1; //标记走过了
	if (solve(1, 1, 1) == false)printf("impossible\n");  //走不到头
	else //可以走到头,输出某一条路径就行
	{
		for (int i = 0; i<idx; i++)printf("(%d,%d) ", path[i][0], path[i][1]);
	}
	return 0;
}

②答案
//水管工游戏答案
#define _CRT_SECURE_NO_WARNINGS    
#include<iostream>
using namespace std;
struct node {
	int x;
	int y;
};//用来记录经过点的坐标

int a[15][15];
bool book[15][15];
node root[15];//记录路径
int Size;
int N, M;
int flag = 0;
//dfs(0, 0, 1);
void dfs(int x, int y, int front) {
	if (x == N - 1 && y == M) {//这里的y值要注意,要超出图边界,水管要出水,出图
		flag = 1;
		for (int i = 0; i<Size; ++i)
			cout << root[i].x << root[i].y << " ";
		return;
	}
	if (x<0 || x >= N || y<0 || y >= M) return;//超过图边界return

	if (book[x][y] == true) return;//标记过return

	book[x][y] = true;//标记

	root[Size].x = x;//把经过的点放入路径栈
	root[Size++].y = y;

	//水管口 左1 上2 右3 下4
	if (a[x][y] >= 5 && a[x][y] <= 6) {//直的水管两种放置方式横放竖放
		if (front == 1)//水管口在左边
			dfs(x, y + 1, 1);//横放向右移动水管口在左边1
		if (front == 2)//水管口在上边
			dfs(x + 1, y, 2);//向下移动水管口在上边
		if (front == 3)//水管口在右边
			dfs(x, y - 1, 3);//水管口在右边
		if (front == 4)//水管口在下边
			dfs(x - 1, y, 4);    //水管口在下边                        
	}

	if (a[x][y] >= 1 && a[x][y] <= 4) {//弯的水管
		if (front == 1) {//类似 特别要注意一下下一次的水管口的位置
			dfs(x + 1, y, 2);
			dfs(x - 1, y, 4);
		}
		if (front == 2) {
			dfs(x, y + 1, 1);
			dfs(x, y - 1, 3);
		}
		if (front == 3) {
			dfs(x + 1, y, 2);
			dfs(x - 1, y, 4);
		}
		if (front == 4) {
			dfs(x, y + 1, 1);
			dfs(x, y - 1, 3);
		}
	}
	book[x][y] = false;
	if (Size > 0) Size--;
	return;
}
int main()
{
	int test_case;
	int T;
	freopen("input.txt", "r", stdin);
	cin >> T;
	for (test_case = 1; test_case <= T; ++test_case)
	{
		cin >> N >> M;
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				a[i][j] = 0;
				book[i][j] = false;
			}
		}
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				cin >> a[i][j]; //存放水管map
			}
		}
		dfs(0, 0, 1);
		if (flag == 0) //能不能走通
		{
			cout << "NO" << endl;
		}
		else
		{
			cout << "YES"<<endl;
		}
	}
	return 0;
}
/*
输入:
1
5 4
5 3 5 3
1 5 3 0
2 3 5 1
6 1 1 5
1 5 5 4
输出:
坐标都-1了,从(0,0)开始不对啊,题目的要求是从(1,1)开始的
(1,1) (1,2) (2,2) (3,2) (3,3) (3,4) (4,4) (5,4)
*/
23、油田探测(求石油块,跟求星空块的个数是一样的,太简单)
//油田探测,上下左右对角线,找方块,m*n的矩阵,*是没石油,@是有石油,求有几个石油块
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int M, N, answer;
int grid[100][100];
int d[8][2] = { { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 },{ 1,1 },{ 1,-1 },{ -1,-1 },{ -1,1 } };
void dfs(int x, int y)
{
	grid[x][y] = 0;
	for (int i = 0; i < 8; i++)
	{
		int xNext = x + d[i][0];
		int yNext = y + d[i][1];
		if (xNext >= 0 && xNext < M&&yNext >= 0 && yNext < N&&grid[xNext][yNext] == 1)
		{
			dfs(xNext, yNext);
		}
	}
}
int main()
{
	freopen("input.txt", "r", stdin);
	int t;
	scanf("%d", &t);
	for (int tc = 1; tc <= t; tc++)
	{
		scanf("%d%d", &M, &N);
		for (int i = 0; i < M; i++)
		{
			for (int j = 0; j < N; j++)
			{
				char x;
				scanf("%c", &x);
				while (x == ' ' || x == '\n')
				{
					scanf("%c", &x);
				}
				if (x == '*')
				{
					grid[i][j] = 0;
				}
				if (x == '@')
				{
					grid[i][j] = 1;
				}
			}
		}
		answer = 0;
		for (int i = 0; i < M; i++)
		{
			for (int j = 0; j < N; j++)
			{
				if (grid[i][j] == 1)
				{
					dfs(i, j);
					answer++;
				}
			}
		}
		printf("#%d %d\n", tc, answer);
	}
	return 0;
}
/*
input:
1
5 5
****@
*@@*@
*@**@
@@@*@
@@**@

output:
#1 2

*/


24、同步移动
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, M, map[25][25], answer;
bool flag[25][25][25][25] = { false };
int qx1[810000], qy1[810000], qx2[810000], qy2[810000], qstep[810000], head, tail;
int dir[9][2] = { { 1,0 },{ -1,0 },{ 0,-1 },{ 0,1 },{ 1,1 },{ 1,-1 },{ -1,1 },{ -1,-1 },{ 0,0 } };
int sx1, sy1, ex1, ey1, sx2, sy2, ex2, ey2;
void add(int x1, int y1, int x2, int y2, int step)
{
	flag[x1][y1][x2][y2] = true;
	qx1[tail] = x1;
	qy1[tail] = y1;
	qx2[tail] = x2;
	qy2[tail] = y2;
	qstep[tail++] = step;
}
void bfs()
{
	//起点进队
	head = 0;
	tail = 0;
	add(sx1, sy1, sx2, sy2, 0);//起点进队
	while (head<tail)
	{
		//出队
		int x1, y1, x2, y2, step;
		x1 = qx1[head], y1 = qy1[head], x2 = qx2[head], y2 = qy2[head];//出队
		step = qstep[head++];
		//2个物体同时找到了终点
		if (x1 == ex1&&y1 == ey1&&x2 == ex2&&y2 == ey2) //找到了终点就停止啊
		{
			answer = step;
			break; //不能少
		}
		//8个方向+原地不动
		for (int i = 0; i < 9; i++) //8个方向和不动都试一下
		{
			int x1_new = x1 + dir[i][0];
			int y1_new = y1 + dir[i][1];
			if (x1_new<0 || x1_new >= N || y1_new<0 || y1_new >= M)continue; //越界了不管
			if (map[x1_new][y1_new] == -1) continue; //是墙壁不行
			for (int j = 0; j < 9; j++) //8个方向和不动都试一下啊
			{
				int x2_new = x2 + dir[j][0];
				int y2_new = y2 + dir[j][1];
				if (x2_new<0 || x2_new >= N || y2_new<0 || y2_new >= M)continue;
				if (map[x2_new][y2_new] == -1) continue;
				if (flag[x1_new][y1_new][x2_new][y2_new] == true) continue;
				//精华在这一行的判断上,牢牢记住,物体1和物体2不相邻,就是距离的平方>2
				if ((x1_new - x2_new)*(x1_new - x2_new) + (y1_new - y2_new)*(y1_new - y2_new)>2)
				{
					add(x1_new, y1_new, x2_new, y2_new, step + 1);
				}
			}
		}
	}
}
int main()
{
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{
		scanf("%d %d", &N, &M);
		for (int i = 0; i <N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				scanf("%d", &map[i][j]);
				if (map[i][j] == 1)
				{
					sx1 = i;
					sy1 = j;
				}
				if (map[i][j] == 2)
				{
					sx2 = i;
					sy2 = j;
				}
				if (map[i][j] == 3)
				{
					ex1 = i;
					ey1 = j;
				}
				if (map[i][j] == 4)
				{
					ex2 = i;
					ey2 = j;
				}
			}
		}
		answer = -1;
		bfs();
		printf("#%d %d\n", tc, answer);
	}
	return 0;
}
/*
Input
1
14 18
-1 4 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 3
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 2



Sample output:

#1 47
*/

优化版本的同步移动
//优化1:编号从1开始的,把第N+1行全部赋值-1,这样-1是墙,肯定不进队
//优化2:每一个方块编号,判断1和3在不在同一方块,2和4在不在同一方块,只有有不在的直接返回-1,不需要bfs
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define MAXEDGE 25
#define MAXBOUND 27
int map[MAXBOUND][MAXBOUND];
char visit[MAXBOUND][MAXBOUND][MAXBOUND][MAXBOUND];
char sets[MAXBOUND][MAXBOUND];
typedef struct tagNode
{
	int x1;
	int y1;
	int x2;
	int y2;
	int step;
}Node;
Node queue[MAXEDGE*MAXEDGE*MAXEDGE*MAXEDGE];
int N, M;
int s1i, s1j, s2i, s2j; //2个起点坐标
int e1i, e1j, e2i, e2j;  //2个终点坐标
int dir[9][2] = { -1, 0, -1, 1, 0, 1, 1, 1, 1, 0, 1, -1, 0, -1, -1, -1, 0, 0 };//8个方向+原地不动

void init() //初始化,最开始都没走过
{
	int i, j, p, q;
	for (i = 1; i <= N; i++)
	{
		for (j = 1; j <= M; j++)
		{
			for (p = 1; p <= N; p++)
			{
				for (q = 1; q <= M; q++)
				{
					visit[i][j][p][q] = 0;
				}
			}
		}
	}
}

int bfs()
{
	int head = 0;
	int tail = 0;
	int step = -1;
	int k1, k2, x1, y1, x2, y2;
	Node cur;
	init();

	queue[tail].x1 = s1i;
	queue[tail].y1 = s1j;
	queue[tail].x2 = s2i;
	queue[tail].y2 = s2j;
	queue[tail++].step = 0;
	visit[s1i][s1j][s2i][s2j] = 1;

	while (head < tail)
	{
		cur = queue[head++];
		//两个点都到达终点了
		if (cur.x1 == e1i && cur.y1 == e1j && cur.x2 == e2i && cur.y2 == e2j)
		{
			step = cur.step; break;
		}
		for (k1 = 0; k1 < 9; k1++)//物体1把8个方向+原地不动都试一遍
		{
			x1 = cur.x1 + dir[k1][0];
			y1 = cur.y1 + dir[k1][1];
			if (map[x1][y1] == -1) continue;
			for (k2 = 0; k2 < 9; k2++) //物体2也把8个方向+原地不动试一遍,
			{
				x2 = cur.x2 + dir[k2][0];
				y2 = cur.y2 + dir[k2][1];
				if (map[x2][y2] == -1) continue;
				if (visit[x1][y1][x2][y2] == 1) continue;
				if ((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2) > 2) //当两个坐标的距离大于2进队??
				{
					visit[x1][y1][x2][y2] = 1;
					queue[tail].x1 = x1;
					queue[tail].y1 = y1;
					queue[tail].x2 = x2;
					queue[tail].y2 = y2;
					queue[tail++].step = cur.step + 1;
				}
			}
		}
	}
	return step;
}

void dfs(int x, int y, int set) //找到一块联通的,坐标放到sets里面
{
	int i, j, k;
	sets[x][y] = set; //标记(x,y)在第set个连通块里面(非0就证明是一个连通块)
	for (k = 0; k < 8; k++)
	{
		i = x + dir[k][0];
		j = y + dir[k][1];
		if (map[i][j] != -1 && sets[i][j] == 0) //是1并且没有走过
		{
			dfs(i, j, set); //走它
		}
	}
}

int check()
{
	int i, j;
	for (i = 1; i <= N; i++) //每次找到一块都初始化为0,初始化连通块全0
	{
		for (j = 1; j <= M; j++)
		{
			sets[i][j] = 0;
		}
	}
	int no = 0;
	for (i = 1; i <= N; i++)  //遍历整个map
	{
		for (j = 1; j <= M; j++)
		{
			if (map[i][j] != -1 && sets[i][j] == 0) 找到一个1说明是一块,进去啊
			{
				no++; //第no个连通块
			dfs(i, j, no); //继续dfs
			}
		}
	}
	//如果1和3不在同一连通块或者 2和4不在同一连通块,说明找不到的,直接不需要bfs了
	if (sets[s1i][s1j] != sets[e1i][e1j] || sets[s2i][s2j] != sets[e2i][e2j]) return 0;
	return 1; //否则说明在同一区域可以走看看
}

int solution()
{
	if (check() == 0) return -1;  //1和3不在一个连通块||2和4不在一个连通块,说明找不到
	return bfs(); //能找到,用bfs来找
}

int main()
{
	int tc, tn, i, j, ans;
	for (i = 0; i < MAXBOUND; i++) map[i][0] = map[0][i] = -1;
	scanf("%d", &tn);
	for (tc = 1; tc <= tn; tc++)
	{
		scanf("%d %d", &N, &M);
		for (i = 1; i <= N; i++)
		{
			for (j = 1; j <= M; j++)
			{
				scanf("%d", &map[i][j]);
				if (map[i][j] > 0)
				{
					if (map[i][j] == 1)  //1走到3,2走到4
					{
						s1i = i; s1j = j;
					}
					else if (map[i][j] == 2)
					{
						s2i = i; s2j = j;
					}
					else if (map[i][j] == 3)
					{
						e1i = i; e1j = j;
					}
					else
					{
						e2i = i; e2j = j;
					}
				}
			}
			map[i][j] = -1;//啥意思
		}
		//加外边框,现在i的值是N+1,这一行全部赋值-1就,判断的时候自动不会进队了
		for (j = 1; j <= M + 1; j++) map[i][j] = -1; // N*M的矩阵,给一个外边框
		ans = solution();
		printf("#%d %d\n", tc, ans);
	}
	return 0;
}

25、moving distance

26、find some biggest(week2)

27、二分查找Fn

28、接雨水



29、机器人是否相撞
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>

struct Line { //线段的类型,起点和终点位置
	int type;
	int x1, y1;
	int x2, y2;
};

int n;
Line line[100];
//线a和线b是否相交了
bool intersect(Line a, Line b) {
	if (a.type == b.type) {    //两条线平行
		if (a.type == 0) {   //两条都是水平线
		//不相交:不在同一水平线/横坐标最小和最大分开(终点<起点)
		//如果在同一水平线直接不看后面,只有不在才会继续判断
		//横坐标a的终点小于b的起点/a的起点大于b的终点
			return !(a.y1 != b.y1 || a.x2 <= b.x1 || a.x1 >= b.x2);  
		}
		else {   //两条都是垂直线
		//不相交:不在同一垂直线/纵坐标a的终点小于b的起点/a的起点大于b的终点
			return !(a.x1 != b.x1 || a.y2 <= b.y1 || a.y1 >= b.y2);
		}
	}
	else {
		if (a.type == 0) {   //a是水平线,b是垂直线
		//相交:垂直线b的横坐标小于a的起点/大于a的终点  ||水平线a的y不在垂直线b的y的范围内
			return !(b.x1 <= a.x1 || b.x1 >= a.x2 || a.y1 >= b.y2 || a.y1 <= b.y1);
		}
		else {   //a是垂直线,b是水平线
		//a和b颠倒照抄啊
			return !(a.x1 <= b.x1 || a.x1 >= b.x2 || b.y1 >= a.y2 || b.y1 <= a.y1);
		}
	}

	return 0;
}

int main() {
	printf("\n");
	int t, T; //T个case
	int x, y;
	int tx, ty;
	int d, l;
	int flag;

	scanf("%d", &T);
	t = 0;

	while (t++ < T) {

		scanf("%d", &n);  //走n步
		flag = -1;  //没交叉,相当于answer

		x = y = 0;   //起点标记0
		for (int i = 0; i < n; i++) {  //遍历n步的方向和距离
			scanf("%d%d", &d, &l);

			if (flag != -1) continue;  

			if (d == 1) {    //往北
				//N
				tx = x, ty = y + l;

				line[i].type = 1;
				line[i].x1 = line[i].x2 = x; //x坐标不变
				line[i].y1 = y, line[i].y2 = ty; //y坐标起点不变,终点更新
			}
			else if (d == 2) {
				//S
				tx = x, ty = y - l;

				line[i].type = 1;
				line[i].x1 = line[i].x2 = x;
				line[i].y1 = ty, line[i].y2 = y;
			}
			else if (d == 3) {
				//E
				tx = x + l, ty = y;
				line[i].type = 0;
				line[i].x1 = x, line[i].x2 = tx;
				line[i].y1 = line[i].y2 = y;

			}
			else {
				//W
				tx = x - l, ty = y;

				line[i].type = 0;
				line[i].x1 = tx, line[i].x2 = x;
				line[i].y1 = line[i].y2 = y;
			}

			x = tx, y = ty;
			for (int j = 0; j < i; j++) { //从第一条线开始遍历到当前这条线之前一条
				if (intersect(line[j], line[i])) { //如果当前这个线line[i]跟之前的线j发生交叉
					flag = i + 1; //发生交叉了,记录交叉点是i+1(从0开始记的),不需要再找了
					break;
				}
			}

		}
		printf("#%d %d\n", t, flag);
	}
	return 0;
}


30、培训题mex sequence
// mex(∅)=0, mex({1,2})=0, mex({0,2})=1, mex({0,1,2,4,6})=3.
// When a natural number K is provided, there is sequence gK, (see below).
// g(K,i) = 0 (0 ≤ i < K)
// g(K,i) = mex({g(K,i-⌊i/K⌋), ⋯, g(K.i-1) })  ( i ≥ K)(the mex of the numbers before a total of ⌊i/K⌋ numbers)

#include <stdio.h>
#include <string.h>
using namespace std;

int n, k;

int solve() {
    while (n % k) {
        int x = n / k + 1;
        int d = n - n / k * k;
        int y = d / x + (d % x != 0);
        n -= x * y;
    }
    return n / k;
}

int main() {
    int t, T;
    scanf("%d", &T);
    t = 1;
    while (T--) {
        scanf("%d%d", &n, &k);
        printf("#%d %d\n", t++, solve());
    }

    return 0;
}



31、红绿灯
//红黄蓝交通灯
//给三种颜色的零件,每种最多各10个,做一盏灯需要的零件个数不一样,问最多可以做多少盏?

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, redN, yellowN, blueN, Need[100][3], answer;//有N种类型的灯,每一种需要的三种颜料
int ans[100] = { 0 };//存放每盏灯造了多少个
void dfs(int num,int r,int y,int b) //当前造了num盏灯,选的第step个
{
	if (r < 0 || y < 0 || b < 0)return; //原料没有了
	if (num > answer)  //当前构造了num盏灯
	{
		answer = num;//这里是不能return的
	}
	//每种灯最多制作3个
	for (int i = 0; i < N;i++) //但是一个能选多次,回溯是不行,但是这样写是从最开始往后试,争取让前面的抽最多
	{
		//if (r - Need[i][0] < 0 || y - Need[i][1] < 0 || b - Need[i][2] < 0) continue;
		if (ans[i]>=3)continue; 
		ans[i]++;//选这个原材料
		dfs(num+1, r - Need[i][0], y - Need[i][1], b - Need[i][2]);
		ans[i]--;//不选啊
	}
}
int main()
{
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d",&T);
	for (int tc = 1; tc <= T; tc++)
	{
		//有N种类型的灯,红色、黄色、蓝色的原材料的数量
		scanf("%d %d %d %d",&N,&redN,&yellowN,&blueN);
		//输入N种灯需要这3种颜色的原料分别多少个
		for (int i = 0; i < N;i++)
		{
			scanf("%d%d%d",&Need[i][0],&Need[i][1],&Need[i][2]);
		}
		answer = 0;
		dfs(0,redN,yellowN,blueN);
		printf("#%d %d\n",tc,answer);
	}
	return 0;
}

/*
TC:
4

2 10 10 10
3 2 2
1 1 1

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

2 10 10 10
3 2 2
1 1 1

2 5 5 5
1 1 1
2 1 0


输出:
5
8
5
4
*/


32、经典迷宫问题(经典BFS问题)
//帮助joe走出迷宫,Joe每分钟可以走到上下左右4个方向的相邻格,所有着火的格子会往四周蔓延
//障碍格joe和火都不能进入,当joe走到一个迷宫的边界格子时,认为走出了迷宫
//R*C,#代表墙壁和障碍物,.空地,J是joe的初始位置(也是一个空地),F是着火点,每组数据的迷宫中恰好有一个格子是J
//输出走出迷宫的最短时间(分钟),如果不能走出迷宫,输出IMPOSSIBLE
//思路:火每分钟咋蔓延,用BFS,因为起初有火的格子不止1个,每过一分钟,火周围的空格就变成了火,变火的位置是确定的
//那在这一分钟joe可以往哪儿走,所以就是先把火进队,再看joe周围是不是
//2种解题方法:
//1是预处理一遍每个格子起火的时间,然后再joe进行bfs的时候多加上一个判断就行(必须比火早到)
//2是只用一个bfs,每过一分钟,把火进队,最后再把joe进队,每次先跑火再跑人,vis[i][j][0]火走没走过,vis[i][j][1]joe走没走过
//先bfs一遍所有的火,标记所有的火是第几分钟烧到周围的空地
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int N, M, answer;
int map[100][100];
int vis[100][100][2] = { 0 };
int times[100][100];//初始化为最大值,因为人必须在这之前走
int head, tail, qx[10000], qy[10000], qstep[10000], Jx, Jy;
int dir[4][2] = { { -1,0 },{ 1,0 },{ 0,-1 },{ 0,1 } };
void add(int x, int y, int step)
{
	qx[tail] = x;
	qy[tail] = y;
	qstep[tail++] = step;
}
int findBox(int x, int y)
{
	for (int i = 0; i < N; i++)
	{
		if ((map[i][0] == 1 && x == i&&y == 0) || (map[i][M - 1] == 1 && x == i&&y == M - 1))
		{
			return 1;
		}
	}
	for (int j = 0; j < M; j++)
	{
		if ((map[0][j] == 1 && x == 0 && y == j) || (map[N - 1][j] == 1 && x == N - 1 && y == j))
		{
			return 1;
		}
	}

	return 0;
}
int main()
{
	int T;
	freopen("input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++)
	{
		head = tail = 0;
		answer = -1;
		scanf("%d%d", &N, &M);
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				times[i][j] = 9999999;
				char x;
				scanf("%c", &x);
				while (x == '\n')
				{
					scanf("%c", &x);
				}
				if (x == '.')
				{
					map[i][j] = 1;
				}
				if (x == '#')
				{
					map[i][j] = 0;
				}
				if (x == 'F')
				{
					add(i, j, 0);
					times[i][j] = 0;
					vis[i][j][0] = 1;
					map[i][j] = 2;
				}
				if (x == 'J')
				{
					Jx = i;
					Jy = j;
					map[i][j] = 3;
					vis[i][j][1] = 1;
				}
			}
		}
		//BFS
		while (head < tail)
		{
			int x = qx[head];
			int y = qy[head];
			int step = qstep[head++];
			//先把火周围的火都进队,标记每一个位置火会在第几次烧到
			for (int i = 0; i < 4; i++)
			{
				int xNext = x + dir[i][0];
				int yNext = y + dir[i][1];
				if (xNext< 0 || xNext >= N || yNext<0 || yNext >= M || vis[xNext][yNext][0])continue;
				if (map[xNext][yNext] == 1) //不能越界
				{
					add(xNext, yNext, step + 1);
					times[xNext][yNext] = step + 1;
					vis[xNext][yNext][0] = 1;
				}
			}

		}	
		//所有的点都已经被标记到times了,现在来处理joe同学
		head = tail = 0;
		//Joe的初始位置进队
		add(Jx, Jy, 0);
		vis[Jx][Jy][1] = 1;
		//BFS
		while (head < tail)
		{
			int x = qx[head];
			int y = qy[head];
			int step = qstep[head++];
			//如果已经走到了边界
			if (findBox(x, y))
			{
				answer = step;
				break;
			}
			//4个方向,人必须在当前时刻比火先到,这样才能进队
			for (int i = 0; i < 4; i++)
			{
				int xNext = x + dir[i][0];
				int yNext = y + dir[i][1];
				if (xNext < 0 || xNext >= N || yNext<0 || yNext >= M || vis[xNext][yNext][1])continue;
				if (map[xNext][yNext] == 1 && step + 1<times[xNext][yNext]) //不能越界,这一步必须比火小,相等都不行
				{
					add(xNext, yNext, step + 1);
					vis[xNext][yNext][1] = 1;
				}
			}
		}
		if (answer != -1)
		{
			printf("#%d %d\n", tc, answer);
		}
		else
		{
			printf("#%d IMPOSSIBLE\n",tc);
		}
	}
	return 0;
}
/*
3
5 5
.#...
.F#..
#..J#
..#.#
..#..

5 6
#.#...
#.....
..F#J#
#.#...
..#.#.

5 6
#.#.#.
#....#
..F#J#
#.#..#
..#.#.

output:
#1 2
#2 2
#3 IMPOSSIBLE
*/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值