16年的题目大汇总

1、水流
//给定K步,每一步有一定数量的水管可以走,求这K步走完可以流过哪些水管
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int M, N, X, Y, K, matrix[500][500], flag[500][500], answer;
bool pipeLinks[8][4] = {{ false, false, false, false },{ true, true, true, true },{ true, true, false, false },{ false, false, true, true },
{ true, false, false, true },
{ false, true, false, true },
{ false, true, true, false },
{ true, false, true, false } };//上下左右四个方向的水管是否相通
void flow(int x, int y, int length)
{
	//flag[x][y] = length; //走到点(x,y)的时候还有length步可以走
	if (length == K+1)
	{
		return;
	}
	flag[x][y] = length;   //坐标(x,y)可以在第length步走到
	// up
	//水向上流动,当前的水管有向上的通路,上面的水管有向下的通路;并且走到上一个水管后剩下的步数比length-1小
	if (pipeLinks[matrix[x][y]][0] && x - 1 >= 0 && pipeLinks[matrix[x - 1][y]][1]) {
		flow(x - 1, y, length+1);
	}
	// down
	if (pipeLinks[matrix[x][y]][1] && x + 1 < M && pipeLinks[matrix[x + 1][y]][0]) {
		flow(x + 1, y, length + 1);
	}
	// left
	if (pipeLinks[matrix[x][y]][2] && y - 1 >= 0 && pipeLinks[matrix[x][y - 1]][3]) {
		flow(x, y - 1, length + 1);
	}
	// right
	if (pipeLinks[matrix[x][y]][3] && y + 1 < N && pipeLinks[matrix[x][y + 1]][2]) {
		flow(x, y + 1, length + 1);
	}
}
int stat() {
	int answer = 0;
	for (int i = 0; i < M; i++) {
		for (int j = 0; j < N; j++) {
			if (flag[i][j]!=0) {//所有非0都是走过的
				answer++;
			}
		}
	}
	return answer;
}


int main()
{
	int T;
	scanf("%d",&T);
	for (int test_case = 1; test_case <= T; test_case++)
	{
		answer = 0;
		scanf("%d %d %d %d %d", &M, &N, &X, &Y, &K);  //M*N的矩阵,起点是(X,Y),K是水流步数
		for (int i = 0; i < M; i++)
		{
			for (int j = 0; j < N; j++)
			{
				scanf("%d", &matrix[i][j]);
			}
		}
		flow(X, Y,1);
		answer = stat();
		printf("#%d %d\n", test_case, answer);
	}
	system("pause");
	return 0;
}



2、闯关游戏
//闯关游戏,过关、雇佣或打架,最少需要多少钱能够通过所有关卡
#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);
	}
}


3、爬山
//爬山stepHeight
//多次BFS,但是由于每次跳的高度任意,最高限制为stepHeight,所以前面进队元素可以反复使用,只需要队头置空,队尾不需要置空
//每次换一个stepHeight的时候必须要队头置空啊,因为跳跃的高度是任意的,所以前面stepheigit进队的元素可以重复使用,不需要出队
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int N, M;//N*M的矩阵,N行M列
bool grid[5000][5000];  //矩阵能不能走
int endX, endY; //终点的坐标
bool flag[5000][5000]; //走没走过
int queueX[5000], queueY[5000]; //队列X、Y
int head, tail; //队头和队尾
int answer;
extern void add(int yNext, int xNext);

bool move(int x, int y, int stepHeight) {
	//每次队头出队得到一个 x,y,判断在当前stepheight下能到达哪些点
	if (endX == x && endY == y) { //成功到达终点
		return true;
	}
	//向下跳,任意高度,限制最大stepheight
	for (int xNext = x + 1; xNext <= x + stepHeight; xNext++) {
		if (xNext >= 0 && xNext < N && grid[xNext][y] && !flag[xNext][y]) {  // 没越界、可以走、没走过
			add(y, xNext);   //进队
		}
	}
	//向上跳
	for (int xNext = x - 1; xNext >= x - stepHeight; xNext--) {
		if (xNext >= 0 && xNext < N && grid[xNext][y] && !flag[xNext][y]) {   //没越界、可以走、没走过
			add(y, xNext);   //进队
		}
	}
	//向右跳,只有连续的1才能移动
	if (y + 1 < M && grid[x][y + 1] && !flag[x][y + 1]) {  //没越界、可以走、没走过
		add(y + 1, x);  //进队
	}
	//向左跳,只能跳一下
	if (y - 1 >= 0 && grid[x][y - 1] && !flag[x][y - 1]) {  //没越界、可以走、没走过
		add(y - 1, x);   //进队
	}
	return false;  //所有方向试了还没找到终点
}

void add(int yNext, int xNext) {
	queueX[tail] = xNext; //队尾进队
	queueY[tail] = yNext;
	tail++;
	flag[xNext][yNext] = true;  //走过了
}

int main()
{
	int T;
	int t;
	scanf("%d", &T);
	for (int test_case = 1; test_case <= T; test_case++) 
	{
		scanf("%d %d", &N, &M);
		//初始化队头、队尾
		head = 0;
		tail = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				flag[i][j] = false;//最开始都没走过
				scanf("%d", &t);
				if (t == 0)
				{
					grid[i][j] = false;//0不能走
				}
				if (t == 1)
				{
					grid[i][j] = true;//1可以走
				}
				if (t == 2)
				{
					grid[i][j] = true;//2可以走,是起点,进队
					add(j,i);
				}
				if (t == 3)
				{
					grid[i][j] = true;//3可以走,是终点
					endX = i;
					endY = j;
				}
			}
		}
		//初始化答案为-1
		answer = -1;
		for (int stepHeight = 1; stepHeight < M; stepHeight++)
		{
			head = 0; //每次换一个stepHeight的时候必须要队头置空啊,因为跳跃的高度是任意的,所以前面stepheigit进队的元素可以重复使用,不需要出队
			while (tail > head)
			{
				if (move(queueX[head], queueY[head], stepHeight)) //队头出队
				{  
					//在当前的stepheight可以到达终点,暂停,因为后面的一定比当前大
					answer = stepHeight;
					goto finish;
				}
				head++;  
			}
		}
		finish:printf("#%d %d", test_case, answer);
	}
	system("pause");
	return 0;
}


4、雷电
//雷电
//飞机在移动之前可以使用一次炸弹,遇见金币+1,遇见敌人-1,收集到的金币==-1时爆炸
//输出收集到金币最大/-1(任何形式都活不下来),C硬币、R敌人,5列固定,N行不固定,收集完飞机那一行置空

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
int N, bombstart, answer;
int gameArea[12][5]; //最高12行,固定5列
extern void move(int x, int y, int coins);
void dfs(int x, int y, int coins) //以这为起点又可向左向右不动三种选择
{
	move(x - 1, y, coins); //不动,游戏区域上移一行
	if (y-1>=0)//左边还有空位,游戏区域往左走再上移一行 
	{
		move(x - 1, y - 1, coins);  //当前还能向左移动,那就往左移动一个
	}
	if (y+1<=4)//右边还有空位,游戏区域往右走再上移一行
	{
		move(x - 1, y + 1, coins);
	}
}
void move(int x, int y, int coins)
{
	// 通过游戏区域后
	if (x == -1) {  //0第一行都移动完了,又进来了
		if (coins > answer)   //一番移动结束,跟之前的情况对比取最大
		{
			answer = coins;
		}
		return;
	}

	if (coins + x + 1 <= answer)    //上面还有x+1行等待移动,假设每一行都能获得1个金币,还是比答案小,剪枝
	{
		return;
	}

	if (gameArea[x][y] == 1)    //如果当前这个位置是1,金币+1,继续往下走
	{
		dfs(x, y, coins + 1);
	}
	else if (gameArea[x][y] == 0)  //如果当前位置是0,金币不变,继续往下走
	{
		dfs(x, y, coins);
	}
	else   //如果是2(敌人),要用炸弹了
	{
		//bombstart是第几次移动前爆炸消灭5*5的游戏区域内所有的敌人
		//说是从当前的位置到上面5行的敌人全部炸掉
		//炸弹开始的位置bombstart属于(x-5,x],从x开始网上5行的都消灭
		//x、x-1、x-2、x-3、x-4这五行消灭就行
		if (bombstart > x - 5 && bombstart <= x)  //用炸弹会消灭游戏区域内的所有敌人
		{
			dfs(x, y, coins); //用了炸弹,本该爆炸现在不会减少了,当空位处理
		}
		else  //不用炸弹,遇见敌人了啊
		{
			coins--;  //金币数-1
			if (coins < 0)   //一旦金币数变成-1
			{
				return;  //爆炸,没移动完,coins也不需要保留
			}
			else  //没减少成负数
			{
				dfs(x, y, coins);  //继续往下走
			}
		}
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	for (int tc = 1; tc <= t; tc++)
	{
		scanf("%d",&N);
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < 5; j++)
			{
				scanf("%d",&gameArea[i][j]);
			}
		}
		answer = -1;//求收集到的最大金币数
		//每次移动之前都能使用炸弹,但是也能选择不使用炸弹
		//比如N=7时,有N-5==2行需要移动,可以在第1、2步移动之前用炸弹,也可以不用炸弹吧
		for (bombstart = 0; bombstart <= N - 5; bombstart++)   
		{
			move(N - 1, 1, 0);  //向左一步
			move(N - 1, 2, 0);  //不动
			move(N - 1, 3, 0);   //向右一步
		}
		printf("#%d %d\n",tc,answer);
	}
	system("pause");
	return 0;
}

5、采矿
//再写一遍采矿
//1是公路,0没有公路,C是矿点,任意有公路的非矿点可以作为处理中心,最大处理距离的最小值
//BFS往下的时候,必须是公路并且是没走过没越界的,矿点一定在公路上,非公路不能走的

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<stdlib.h>
int map[21][21], answer,C,N;//C是矿点的个数,C[][]存放矿点的坐标
int mines[4][2]; //矿点坐标,数组名称和变量不能一样
bool flag[21][21];//标记
//队列
int qx[400], qy[400], qstep[400];//走到第几步
int head, tail;
bool isMine(int x, int y)
{
	for (int i = 0; i < C; i++) //遍历C个矿点的坐标
	{
		if (mines[i][0] == x&&mines[i][1] == y)return true;
	}
	return false;
}
void add(int x,int y,int step) //进队并且标记
{
	qx[tail] = x;
	qy[tail] = y;
	qstep[tail] = step;
	tail++;
	flag[x][y] = true;
}
int main()
{
	int t;
	scanf("%d", &t);
	for (int tc = 1; tc <= t; tc++)
	{
		//N*N的矩阵,先输入N,C,C是矿点的个数
		scanf("%d %d",&N,&C);
		for (int i = 0; i < C;i++)//坐标从1开始
		{
			scanf("%d%d",&mines[i][0],&mines[i][1]);
		}
		for (int i = 1; i <= N; i++)
		{
			for (int j = 1; j <= N; j++)
			{
				scanf("%d", &map[i][j]);//map
				flag[i][j] = false; //初始化
			}
		}

		//求最大处理距离的最小值
		answer = 999999999;//求最小初始化为最大
		for (int i = 1; i <= N; i++)
		{
			for (int j = 1; j <= N;j++)
			{
				//找处理中心,非矿点的公路可以作为处理中心
				if (map[i][j] == 1 && !isMine(i, j))
				{
					//找到了一个处理中心,初始化一下各种数据
					//每次一个新的处理中心求到每个矿点的最大处理距离,flag得变false吧
					for (int a = 1; a <= N; a++)
					{
						for (int b = 1; b <= N; b++)
						{
							flag[a][b] = false;
						}
					}
					head = tail = 0;
					int currmines = 0;//当前处理了几个矿
					int maxDis = 0;//最大处理距离(BFS每次肯定是最短路径,取到C个矿最短路径的max)
					//这个处理中心进队一下啊
					add(i, j, 0);
					//不断进出队进行判断
					while (head < tail)
					{
						//出队判断
						int currx = qx[head];
						int curry = qy[head];
						int currstep = qstep[head];
						head++;
						if (isMine(currx, curry))//当前这个就是矿场
						{
							currmines++;//进来先自增
							if (currstep > maxDis)//如果当前最短距离是更大的
							{
								maxDis = currstep;//赋值给最大处理距离
							}
							//当前最短距离比答案还大,直接剪枝
							if (currstep >= answer)break;
							if (currmines == C)//找到了所有的矿
							{
								if (maxDis < answer)  //取最大处理距离的最小,换下一个处理中心
								{
									answer = maxDis;
									break;
								}
							}
							
						}
						
						//其他4个方向继续进队啊 ,是公路没走过且没越界才能进队啊,矿点一定在公路1的位置里面定的(这是题目前提?)
						if (currx - 1 >= 1&&map[currx-1][curry]==1&&!flag[currx-1][curry]) //向上一步没越界并且没走过,进队
						{
							add(currx - 1, curry, currstep + 1);
						}
						if (currx + 1 <= N&&map[currx +1][curry] == 1 &&!flag[currx+1][curry])
						{
							add(currx + 1, curry, currstep + 1);
						}
						if (curry - 1 >= 1 && map[currx ][curry - 1] == 1 && currx <= N&& !flag[currx][curry-1])
						{
							add(currx, curry - 1, currstep + 1);
						}
						if (curry + 1 <= N&&map[currx][curry +1] == 1 && !flag[currx][curry+1])
						{
							add(currx, curry + 1, currstep + 1);
						}
					}
				}
			}
		}


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


6、Base_station
//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、钓鱼
//钓鱼,限制进入的顺序必须是离大门最近的,不限制就更简单了啊,不需要比来比去的
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int pos[3];//存放3个门的位置
int people[3];//存放3个门后的人数
bool vis[3];//3个门有没有走过
int select[3];//开门顺序
int N, answer;  // N个钓鱼位置,求最短路径
int visited[66];//钓鱼的位置最大为60
//handle(0,0);
void handle(int step, int cost)
{
	if (cost >= answer) //当前花费大于answer
	{
		return;
	}
	if (step == 3)//所有门都开完了又进来
	{
		if (cost<answer)
		{
			answer = cost;
			return;
		}
	}
	//gate_pos是第step次开门的时候门的位置,wait_num是门后的人数
	int gate_pos = pos[select[step]]; //门的位置
	int wait_num = people[select[step]]; //门后的人数


	int distance = 0;//距离左边或者右边的距离,设置为0是因为从当前这个门所在的位置开始走的
	 //左边有空位放左边,右边有空位放右边,因为可能出现有一个人走左边走右边是一样的距离
	 //所以在开当前这个门的时候不走完,留下一个人
	 //可能是最后wait_num==2进去了,然后左右都有位置,最好剩0个人,也可能只有一边有位置剩1个人
	while (wait_num >= 2)
	{
		//位置的编号是从1开始到N的
		//左边有位置放左边
		if (gate_pos - distance >= 1 && visited[gate_pos - distance] == 0)
		{
			cost += distance + 1;
			wait_num--;//门后等待的人数减少
			visited[gate_pos - distance] = step + 1;//当前这个人是坐在这个位置的是step+1号门的人
		}
		//右边有位置放右边
		if (gate_pos + distance <= N&&visited[gate_pos + distance] == 0)
		{
			cost += distance + 1;
			wait_num--;
			visited[gate_pos + distance] = step + 1;
		}
		distance++;
	}
	//可能剩下0个人,也可能剩下1个人
	if (wait_num == 0)
	{
		handle(step + 1, cost);  //门后没人处理下一个门后的人
		return;
	}
	else  //剩下1个人
	{
		//这个人可以放左边也可以放右边

		//左边空?
		int dis1 = distance;//记录下当前已经放到哪个位置了
		bool find1 = false;//看左边有没有空位置
						   //把左边所有的位置都找一个遍,看有没有空位
		while (1)
		{
			if (gate_pos - dis1<1) break; //坐标从1到N,小于1说明到头了break
			if (visited[gate_pos - dis1] == 0)
			{
				find1 = true;
				break;
			}
			dis1++;
		}
		//右边空?
		int dis2 = distance;
		bool find2 = false;
		while (1)
		{
			if (gate_pos + dis2>N) break; //坐标从1到N,大于N说明到头了break
			if (visited[gate_pos + dis2] == 0)
			{
				find2 = true;//右边有空位
				break;
			}
			dis2++;
		}

		//判断完左右边空位状态,分类讨论
		if (find1 == true && find2 == false)
		{
			cost += dis1 + 1;
			visited[gate_pos - dis1] = step + 1;
			handle(step + 1, cost);
			return;
		}
		else if (find1 == false && find2 == true)
		{
			cost += dis2 + 1;
			visited[gate_pos + dis2] = step + 1;
			handle(step + 1, cost);
			return;
		}
		else
		{
			//左右都有位置,需要判断左边还是右边距离更短
			//左边更近放左边
			if (dis1<dis2)
			{
				cost += dis1 + 1;
				visited[gate_pos - dis1] = step + 1;
				handle(step + 1, cost);
				return;
			}
			else if (dis1>dis2)
			{
				cost += dis2 + 1;
				visited[gate_pos + dis2] = step + 1;
				handle(step + 1, cost);
				return;
			}
			else  //距离一样可以往左也可以往右,回溯解决
			{
				cost += dis1 + 1;
				visited[gate_pos - dis1] = step + 1;
				handle(step + 1, cost);

				//回溯,放右边
				visited[gate_pos - dis1] = 0;
				visited[gate_pos + dis2] = step + 1;
				//比如最开始处理1号门。后面继续handle下一步的时候,visited里面的2、3号门要清空一下
				for (int j = 1; j <= N; j++)
				{
					if (visited[j]>step + 1)
					{
						visited[j] = 0;
					}
				}
				handle(step + 1, cost);
				return;
			}
		}
	}
}
void dfs(int step)//确定3个大门的顺序
{
	if (step == 3)//已经定了第3个的顺序又进来了
	{
		for (int i = 1; i <= N; i++)//不初始化会出错
		{
			visited[i] = 0;  //在这种开门顺序下,初始所有的钓鱼位置都没走过
		}
		handle(0, 0);//确定开门顺序后,计算最短的路径
		return;
	}
	for (int i = 0; i<3; i++)
	{
		if (!vis[i])
		{
			//这里用回溯来定顺序
			//当前step选择这个门
			select[step] = i;
			vis[i] = true;
			dfs(step + 1);//继续下一步选门
						  //也可以不选这个门
			vis[i] = false;
		}
	}
}
int main()
{
	int T;
	scanf("%d", &T);
	for (int t = 1; t <= T; t++)
	{
		scanf("%d", &N);      //N个钓鱼位置
		for (int i = 0; i < 3; i++)
		{
			scanf("%d %d", &pos[i], &people[i]); //三个门的位置和门后人的数量
		}
		//初始化,求最短初始化最大
		answer = 0x7FFFFFFF;
		for (int j = 0; j <3; j++)
		{
			vis[j] = false;//3个门的顺序
		}
		dfs(0);//回溯  (定三个大门打开的顺序,A33=6种)
		printf("#%d %d\n", t, answer);
	}
	system("pause");
	return 0;
}


8、广告
//3段广告不能重叠播放,广告被一个人完整听完就能获得价值
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int T, answer, N, L1, L2, L3, P1, P2, P3, persons[50][2];

void calc(int start1, int start2, int start3){
	int result = 0;
	for (int i = 0; i < N; i++){ //遍历每一个人,一个人只能从完整听完的广告里面选价值最大的一个
		int pointsPerson = 0;
		//取3个听完完整广告的人中价值最大的,加起来
		if (persons[i][0] <= start1 && persons[i][1] >= start1 + L1){
			if (pointsPerson < P1){
				pointsPerson = P1;
			}
		}
		if (persons[i][0] <= start2 && persons[i][1] >= start2 + L2){
			if (pointsPerson < P2){
				pointsPerson = P2;
			}
		}
		if (persons[i][0] <= start3  && persons[i][1] >= start3 + L3){
			if (pointsPerson < P3){
				pointsPerson = P3;
			}
		}
		result += pointsPerson;//sum(每个人听广告获得的价值(=max(完整听完的一个广告价值)))
	}

	if (answer < result){
		answer = result;
	}
}

int main(int argc, char** argv) {
	freopen("sample_input.txt", "r", stdin);
	scanf("%d", &T);
	for (int tc = 1; tc <= T; ++tc){
		answer = 0;
		scanf("%d%d%d%d%d%d%d", &N, &L1, &L2, &L3, &P1, &P2, &P3);

		int duration;
		for (int i = 0; i < N; i++){
			scanf("%d %d", &persons[i][0], &duration);
			persons[i][1] = persons[i][0] + duration;//每个人的始末时间
		}

		for (int start1 = 1; start1 <= 50; start1++){
			for (int start2 = 1; start2 <= 50; start2++){   //排除广告交叉的情况
				if (start2 < start1 + L1 && start2 + L2 > start1){ //2开始了1还没结束 并且 2结束之前1早开始了
					continue;
				}
				for (int start3 = 1; start3 <= 50; start3++){
					if (start3 < start1 + L1 && start3 + L3 > start1){  //3开始了1还没结束,3结束之前1早开始了
						continue;
					}
					if (start3 < start2 + L2 && start3 + L3 > start2){ //3开始了2还没结束,3结束之前2早开始了
						continue;
					}
					calc(start1, start2, start3);
				}
			}
		}

		printf("#%d %d\n", tc, answer);
	}
	return 0;
}


9、加油站
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
//最多有8个汽车
int N; //N个汽车
int car[8];//1 Gasoline 2 Diesel
int typeNum[3]; //存放每种类型汽车的个数
bool visit[8]; //有没有走过
int answer;  //最小步数

int num1;  //1号类型的步数
int num2;  //2号类型的步数
int total_steps;   //总步数
int choose[8+ 5];
//dfs(1,0,-1);
//最开始在1号加油站已经用了一步了,step=1,cost=0,pos=-1
void dfs(int step, int cost, int pos) //当前跳动次数、步数开销、位置
{
	if (step == total_steps)  //已经达到总步数了
	{
		if (cost < answer) answer = cost;
		return;
	}
	//上一步在1号加油站,往右找到一个1号没走过的汽车,我可以继续走,也可以跳去2号加油站
	if (choose[step - 1] == -1) //当前这一步往右走
	{
		for (int j = 0; j < N; j++) 
		{
			if (car[j] == 2 || visit[j]) continue; //遇见2号汽车和走过的就忽略
			choose[step] = 1;  //当前这个位置是1号类型的汽车
			visit[j] = true; //加油
			dfs(step + 1, cost + j + 1, j);//继续dfs,从1号加油站到j移动j+1步
			visit[j] = false;  //不加油,试一试下一个位置
		}
		//需要到2号加油站
		if (num2 != 0) 
		{
			choose[step] = -2; //当前这一步去2号加油站  
			num2--; //
			dfs(step + 1, cost + N + 1, N); //去2号加油站要N+1步啊
			num2++; //不去2号加油站
		}
	}
	
	//如果上一步在2号加油站,往左找,我可以去没走过的2号类型汽车位置,也可以去1号加油站
	if (choose[step - 1] == -2)  
	{
		for (int j = N - 1; j >= 0; j--)//往左走
		{
			if (car[j] == 1 || visit[j]) continue; //如果是1号类型的车或者是走过的,忽略
			choose[step] = 2; //这一步选择走到2号类型的车
			visit[j] = true; //这个位置加油
			dfs(step + 1, cost + N - j, j); //继续dfs,从位置N(2号加油站的位置)到j走N-j步
			visit[j] = false; //不加油
		}
		//如果可以跳到1号加油站,我跳到1号加油站试试
		if (num1 != 0)
		{
			choose[step] = -1; //当前这步跳到1号加油站
			num1--;  //1号的步数--
			dfs(step + 1, cost + N + 1, -1);  //2号加油站跳到1号加油站要N+1步
			num1++;
		}
	}

	//上一步我在1号类型汽车那边
	if (choose[step - 1] == 1) 
	{
		//上上一步不在1号类型汽车那边,说明我肯定还有剩余的1号汽油
		if (choose[step - 2] != 1) 
		{
			for (int j = pos + 1; j < N; j++) //往右走
			{
				if (car[j] == 2 || visit[j]) continue; //如果是2或者走过了,忽略
				choose[step] = 1; //选择这一步跳跃到这个1号类型车位置j
				visit[j] = true; //给这个位置加油
				dfs(step + 1, cost + j - pos, j);//继续dfs,移动了j-pos步
				visit[j] = false;//不加油试试
				break; //这是第二次走到1号类型的车位置了,没油了
			}
		}
		if (num1 != 0) //如果1号车还没加完
		{
			choose[step] = -1; //跳到1号加油站
			num1--; 
			dfs(step + 1, cost + pos + 1, -1);
			num1++; //不去1号加油站
		}
		if (num2 != 0) //如果2号车还没加完
		{
			choose[step] = -2; //跳到2号加油站
			num2--;
			dfs(step + 1, cost + N - pos, N);
			num2++;
		}
	}
	
	//如果上一步再2号类型汽车位置
	if (choose[step - 1] == 2)
	{
		if (choose[step - 2] != 2) //上上一步不在2号类型汽车位置,说明还有手里2号汽油可以加
		{
			for (int j = pos - 1; j >= 0; j--)//往左走
			{
				if (car[j] == 1 || visit[j]) continue;//1或走过,忽略
				choose[step] = 2; //走到这个2号类型汽车的位置
				visit[j] = true; //加油
				dfs(step + 1, cost + pos - j, j); //继续加油(第二次了)
				visit[j] = false;//不加油看看
				break;//没有加油机会了,只能加一个
			}
		}
		if (num1 != 0)//如果还有1号车,跳到1号汽车站看看,或不跳
		{
			choose[step] = -1;
			num1--;
			dfs(step + 1, cost + pos + 1, -1);
			num1++;
		}
		if (num2 != 0) //如果还有2号车,跳到2号汽车站看看,或不跳
		{
			choose[step] = -2;
			num2--;
			dfs(step + 1, cost + N - pos, N);
			num2++;
		}
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	for (int t = 1; t <= T; t++)
	{
		scanf("%d", &N); //N个汽车
		typeNum[1] = typeNum[2] = 0; //初始化类型1和2的汽车数量都是0
		for (int j = 0; j < N; j++) 
		{
			scanf("%d", car + j);//N个汽车的类型
			visit[j] = 0;   //初始化都没走过
			typeNum[car[j]]++; //car[j]要么是1要么是2,统计1和2类型的汽车的个数
		}
		//num1是需要去1号加油站的次数,num2是需要去2号加油站的次数
		num1 = (typeNum[1] + 1) / 2; 
		num2 = (typeNum[2] + 1) / 2; 
		//如果1号车0个,1号加油站只需要走1次(第一次从这出发啊),但按上面计算num1是0
		if (typeNum[1] == 0) num1 = 1; 
		//1号加油站经过num1次,2号加油站经过num2次,每一个车最少要走N次,一共跳动这么多次
		total_steps = num1 + num2 + N; 
		answer = 0x7FFFFFFF;

		choose[0] = -1; //最开始从左边-1的位置开始走
		num1--; //num1的次数减少一个了
		dfs(1, 0, -1); //开始第一次跳动

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

10、挖水渠
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
//挖N个水渠,有V行,一行有H块土地
int N,V, H, C1, R1, M1, C2, R2, M2, land[15][500], lineCosts[15], answer;
//求每一行的最小开销
void calcEachLine() {
	for (int line = 0; line < V; line++) {//遍历V行
		int lineCost = 99999999; //每一行最小开始初始化为最大
		//dig1从0到H,就是把所有情况都试了一遍哇!!
		for (int dig1 = 0; dig1 <= H; dig1++) {  //遍历V行的H个土地块
			int dig2 = H- dig1; //挖掘机2反着来
			int cost = 0;
			//挖掘机1从0到dig1-1
			for (int i = 0; i < dig1; i++) {
				cost += C1 * land[line][i];
			}
			//挖掘机2从dig1-2到H-1,j==dig1-1的时候for结束
			for (int j = H - 1; j > dig1 - 1; j--) {
				cost += C2 * land[line][j];
			}
			//如果挖完发现有挖掘机是连着挖了的
			if (dig1 > dig2 + 1)
				cost += (dig1 - dig2 - 1)*R1;

			else if (dig2 > dig1 + 1)
				cost += (dig2 - dig1 - 1)*R2;

			if (lineCost > cost) {
				lineCost = cost;
			}
		}
		lineCosts[line] = lineCost;
	}
}
//找N行水渠,取这N行和的最小开销 dfs(start,0,1);
//当前finish个挖完,当前挖完的是current
void dfs(int current, int totalCost, int finished) {
	totalCost += lineCosts[current]; //当前这一行挖完

	if (totalCost >= answer) { //当前开销已经比answer大了,剪枝
		return;
	}

	if (finished == N) { //第N个都完成了,如果结果比答案小,给答案
		if (totalCost < answer)
		{
			answer = totalCost;
		}
		return;
	}
	//继续往下移动,但是距离必须大于2
	for (int next = current + 2; next <V; next++) {
		dfs(next, totalCost + (M1*M1 + M2*M2)*(next - current), finished + 1);
	}
}

int main() {
	int t;
	scanf("%d", &t);
	for (int tc= 1; tc<=t;tc++) {
		scanf("%d%d%d", &N, &H, &V); //V是行,H是列,先输入列再输入行
		for (int i = 0; i < V; i++) { //V*H的矩阵
			for (int j = 0; j < H; j++) {
				scanf("%d", &land[i][j]);
			}
		}
		scanf("%d%d%d%d%d%d", &C1, &R1, &M1, &C2, &R2, &M2);
		answer = 99999999;//求最小初始化为最大
		calcEachLine();
		//可能从任意一行开始挖,建设第一条的时候没有移动成本
		for (int start = 0; start <V; start++)//V行的任意一个起点都可以开始
		{
			dfs(start,0,1);
		}
		printf("#%d %d\n",tc, answer);
	}
	system("pause");
	return 0;
}

11、轮胎压力测试
//轮胎压力测试
//损坏轮胎:胎压<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;
}

12、生成地图应用程序 generating a stop station
//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 tc = 1; tc <= T;tc++) {
		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", tc, answer);
	}
	return 0;
}

13、黑白棋
//位置的数量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;
}

14、轰炸
//轰炸
//投下N个炸弹,飞机可以来回飞,有W列,一列有H个block,也就是H*W的矩阵,从W列里面找N列轰炸

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
int T, N, W, H, init[15][15], answer;  //N次轰炸,W列,每一列有H个block
extern void findN(int blocks[][15], int steps);
extern void explode(int blocks[][15], bool flag[][15], int x, int y);
extern bool bomb(int blocks[][15], int position);
int main(int argc, char** argv) {
	scanf("%d", &T);
	for (int tc = 1; tc <= T; tc++) {
		scanf("%d%d%d", &N, &W, &H);
		for (int i = 0; i < H; i++) {  //H行,W列
			for (int j = 0; j < W; j++) {
				scanf("%d", &init[i][j]);
			}
		}

		answer = 99999; //求剩下的方块最少是多少,初始化最大
		findN(init, 0);  //从第1列开始找轰炸点

		if (answer == 99999) {   //没统计到剩下的方块数,说明没有剩余的方块了
			answer = 0;
		}

		printf("#%d %d\n", tc, answer);
	}
	system("pause");
	return 0;
}
//寻找第step+1个轰炸点
void findN(int blocks[][15], int steps) {
	if (steps == N) {  //炸到最后一个了又进来
		int result = 0;    //计算当前有多少个方块
		for (int j = 0; j < W; j++) {  //遍历每一列
			for (int i = 0; i < H; i++) {  //找到这一列最上面的一个方块
				if (blocks[i][j] != 0) { //非0是说明是一个方块
					result += H - i;  //这个位置往下,一共有H-i个方块,例如H=3,i=0时,一共有3个方块
					break; //这列不找了,找下一列
				}
			}
		}

		if (answer > result) { //求最小剩余方块数
			answer = result;
		}
		return;
	}

	//有一个疑问,这里为什么不用回溯呢,选N列,我可以选可以不选
	for (int next = 0; next < W; next++) {  //遍历每一列     
		int blocksCopy[15][15];
		for (int i = 0; i < H; i++) {    //复制当前的矩阵
			for (int j = 0; j < W; j++) {
				blocksCopy[i][j] = blocks[i][j];
			}
		}
		//当前这个第next列去轰炸,如果有轰炸点,且轰炸结束了,找下一列轰炸
		//如果当前这一列不能轰炸,继续for循环试下一列能不能作为第step+1次轰炸列
		if (bomb(blocksCopy, next)) {
			findN(blocksCopy, steps + 1);  //找下一个轰炸点
		}
	}
}
bool bomb(int blocks[][15], int position) {
	//初始化每个位置都没走过
	bool flag[15][15];   
	for (int i = 0; i < H; i++) {
		for (int j = 0; j < W; j++) {
			flag[i][j] = false;
		}
	}

	// find the explode block
	int row = -1;
	for (int i = 0; i < H; i++) {  //在position列遍历每一个block的时候找到一个非0
		if (blocks[i][position] != 0) {  //找到了第一个非0的爆炸点就暂停,不要再找了
			row = i;
			break;
		}
	}
	if (row == -1) {  //没有爆炸点,轰炸失败
		return false;
	}
	else {  //有爆炸点,当前那个blocks和全空flag,按照第position列第row行开始炸
		explode(blocks, flag, row, position);

		// fall
		for (int j = 0; j < W; j++) { //遍历每一列
			int write = H - 1;
			for (int read = H - 1; read >= 0; read--) { //从最后一行网上遍历
				if (blocks[read][j] != 0) {  //只要找到一个非0,就要落下来啊
					int value = blocks[read][j];
					blocks[read][j] = 0;
					blocks[write--][j] = value;
				}
			}
		}

		return true;
	}
}
void explode(int blocks[][15], bool flag[][15], int x, int y) {
	flag[x][y] = true;
	for (int dis = 1; dis < blocks[x][y]; dis++) {  //从距离1到当前位置的数字-1遍历,都要轰炸
		if (x - dis >= 0 && !flag[x - dis][y] && blocks[x - dis][y] != 0) {
			explode(blocks, flag, x - dis, y);
		}
		if (x + dis < H && !flag[x + dis][y] && blocks[x + dis][y] != 0) {
			explode(blocks, flag, x + dis, y);
		}
		if (y - dis >= 0 && !flag[x][y - dis] && blocks[x][y - dis] != 0) {
			explode(blocks, flag, x, y - dis);
		}
		if (y + dis < W && !flag[x][y + dis] && blocks[x][y + dis] != 0) {
			explode(blocks, flag, x, y + dis);
		}
	}
	blocks[x][y] = 0;
}



15、选课
//它们好像都没这个题
//给出N(5<=N<=14)个课程的起始时间(8:00-19:00)
//时间格式为HHs MMs MMe ,8<=HH<=18,MM的范围是0,10,20,30,40,50
//AB两个学生来选,每个学生不能选择时间有冲突的课程
//求最后两名学生一共最多能选择多少分钟的课程(每个课程只计算一次)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int T, N, classStart[14], classEnd[14], answer;
bool flagA[66], flagB[66];

void dfs(int current, int count){  //从课程0开始选啊,课程编号从0开始的
	if (current == N){  //N个课程都选完了,又进来
		if (answer < count){  //取最大
			answer = count;
		}
		return;
	}

	bool isFreeA = true, isFreeB = true;  //判断A和B是不是空闲状态,能不能选课
	for (int i = classStart[current]; i < classEnd[current]; i++){  //在这个课程的开始时间到结束时间
		if (flagA[i]) //整个课程过程中A一旦有走过,说明有课,不是空闲的
			isFreeA = false;  
		if (flagB[i]) //整个课程过程中B一旦有走过,说明不是空闲的
			isFreeB = false;
	}

	if (isFreeA){  //A是空闲的,让A来选这个课程,继续dfs,回溯,A不选这个课
		for (int i = classStart[current]; i < classEnd[current]; i++)
			flagA[i] = true; //当前这个课的时间段,A选课了
		dfs(current + 1, count + classEnd[current] - classStart[current]);
		for (int i = classStart[current]; i < classEnd[current]; i++) //回溯
			flagA[i] = false;
	}

	if (isFreeB){ //同理B是空闲的,让B来选这个课程,继续dfs,回溯,B不选这个课
		for (int i = classStart[current]; i < classEnd[current]; i++)
			flagB[i] = true;
		dfs(current + 1, count + classEnd[current] - classStart[current]);
		for (int i = classStart[current]; i < classEnd[current]; i++)
			flagB[i] = false;
	}
	//这个课可能A和B都不选,继续dfs
	dfs(current + 1, count);
}

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++){
			int startHH, startMM, endHH, endMM;
			scanf("%d%d%d%d", &startHH, &startMM, &endHH, &endMM); //开始的小时和秒
			classStart[i] = (startHH - 8) * 60 + startMM ; //在第多少分钟开始
			classEnd[i] = (endHH - 8) * 60 + endMM; //在第多少分钟结束
		}
		for (int i = 0; i < 66; i++){   //最开始A和B都没选课
			flagA[i] = flagB[i] = false;
		}

		answer = -1; //求最多能选择多少分钟的课程
		dfs(0, 0);

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



16、数字重叠
//这个题他们都没有
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
//数字重叠,求拼接出的最长长度
//必须前后有重叠才能拼接,重叠多少可以选择,如果没有一个重叠方案,例如case2,24和123,那么就取最长的那个的长度3进行输出
int T, N, answer, size[9];  //size存放N个字符串各自的长度
char numbers[9][6], merged[50];  //numbers存放N个数字本身,每个数字用字符串存
bool flag[9];  //数字是否访问过

int getSize(char characters[]) {   //计算字符串的长度
	for (int i = 0; i < 50; i++) {
		if (characters[i] == '\0') {
			return i;
		}
	}
	return -1;
}

int checkOverlap(int current) {  //数字current是否存在重叠?
	int mergedSize = getSize(merged);  //当前数字的长度
	if (mergedSize == 0) {  //剪枝!!!没有长度不需要判断
		return 0;
	}

	//先直接拼到一起,然后剪去里面重复的一段
	for (int overlapSize = 1; overlapSize < size[current]; overlapSize++) {  //能重叠的最长长度就是它本身的长度
		bool findResult = true;
		for (int i = 0; i < overlapSize; i++) {
			int indexInMerged = mergedSize - overlapSize + i;  //合并的长度-重复长度+i
			if (merged[indexInMerged] != numbers[current][i]) {//
				findResult = false;
				break;
			}
		}
		if (findResult) {
			return overlapSize;
		}
	}

	return -1;
}

void dfs(int finished) {
	int mergedSize = getSize(merged);  //当前合并后的长度

	bool findNext = false;  //能不能查找下一个
	for (int next = 0; next < N; next++) {   //遍历N个数字
		if (!flag[next]) {                        //如果没走过
			int overlapSize = checkOverlap(next);  // 查看是否重叠,返回重叠数字的长度
			if (overlapSize != -1) {  //-1的时候是找不到重叠数字的,!=-1就是可以拼接
				findNext = true;  //能找到下一个
				flag[next] = true;  //当前走过了
				for (int i = overlapSize; i < size[next]; i++) {  //可以选择重叠的范围是
					merged[mergedSize - overlapSize + i] = numbers[next][i];
				}

				dfs(finished + 1);  //继续拼接下一个啊

				flag[next] = false;  //也可以选择不走啊,回溯
				// 不可以只复原第一个位置
				for (int i = overlapSize; i < size[next]; i++) {
					merged[mergedSize - overlapSize + i] = '\0';
				}
			}
		}
	}

	if (!findNext) {  //找不到下一个可以拼接的了,看一下这次拼接的长度是不是更大
		if (answer < mergedSize) {
			answer = mergedSize;
		}
	}
}

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("%s", &numbers[i]);     //numbers存放N个数字本身
			size[i] = getSize(numbers[i]);   //size数组存放了N个数字的长度
			flag[i] = false;
		}
		for (int i = 0; i < 50; i++) {
			merged[i] = '\0';
		}
		answer = 0;

		dfs(0);

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值