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;
}
16年的题目大汇总
于 2022-06-23 10:07:43 首次发布