A.The Pilots Brothers’ refrigerator
大致题意
给出一个由"-“和”+"组成的 4x4方阵, "-"表示开, “+“表示关, 每次可以选择一个位置改变它的状态, 但是该位置同一行同一列的所有元素都换改变符号, 求经过怎样的步骤能使所有位置都打开(全变成”-”)
分析
数据不大, 暴力搜索, 将求得的路径存在栈中, 在dfs中对栈中元素进行更新即可
代码实现
#include<cstdio>
#include<cstdlib>
int mapp[5][5];
int vis[5][5];
int flag;
typedef struct sta{
int row;
int column;
bool operator==(sta &temp) //重载运算符
{
return (row==temp.row) && (column==temp.column);
}
}sta;
sta stack[20];
sta ans[20];
int sk=1;
int length = 999;
bool isOpen()
{
for(int i=1; i<=4; i++)
{
for(int j=1; j<=4; j++)
{
if(mapp[i][j]==1) return false;
}
}
return true;
}
void change(int x, int y)
{
for(int i=1; i<=4; i++)
{
mapp[x][i] = !mapp[x][i];
}
for(int i=1; i<=4; i++)
{
mapp[i][y] = !mapp[i][y];
}
mapp[x][y] = !mapp[x][y];
return;
}
void copy()
{
for(int i=1; i<sk; i++)
{
ans[i] = stack[i];
}
}
void printmap()
{
for(int i=1; i<=4; i++)
{
for(int j=1; j<=4; j++)
{
printf("%d ", mapp[i][j]);
}
printf("\n");
}
printf("\n");
}
void dfs(int x, int y)
{
// printmap();
int i, j;
for(i=x; i<=4; i++)
{
if(i==x) j = y;
else j = 1;
for(; j<=4; j++)
{
if(!vis[i][j])
{
vis[i][j] = 1;
change(i,j);
stack[sk].row = i;
stack[sk].column = j;
sk ++;
if(isOpen())
{
if(sk<length)
{
length = sk;
copy();
}
}
dfs(i,j);
vis[i][j] = 0;
change(i,j);
sk --;
}
}
}
return;
}
int main(void)
{
char c;
for(int i=1; i<=4; i++)
{
for(int j=1; j<=4; j++)
{
scanf("%c", &c);
if(c=='-') mapp[i][j] = 0;
else mapp[i][j] = 1;
}
getchar();
}
dfs(1,1);
printf("%d\n", length-1);
for(int i=1; i<length; i++)
{
printf("%d %d\n", ans[i].row, ans[i].column);
}
return 0;
}
收获与反思
这段代码if(i==x) j = y; else j = 1;
可以说是这道题dfs中可行的最关键的两条语句. 本质原因还是对dfs不太熟练, 每次递归的元素不是很清楚.
另外从这道题学到了将字符数组转化成"01"数组的小技巧, 的确操作起来会更方便(虽说空间使用会更大)
B. Fractal
大致题意
给出一个"一阶"图形: X
之后的每高一阶即是将上一阶图形分别放在左上右上中间左下右下五个位置
分析
打表, 用一个三维数组储存每一阶图形, 需要的地方直接打印出来即可 (更聪明的做法是只用一个二维数组保存最高阶图形, 前面阶数的图形可以看成该最高阶图形左上角的某一部分)
代码实现
#include<cstdio>
#include<cmath>
char mapp[1000][1000][8] = {0};
void copy1(int bx, int by, int m, int nowi)
{
for(int i=1, bi=bx; i<=m; i++, bi++)
{
for(int j=1, bj=by; j<=m; j++, bj++)
{
mapp[bi][bj][nowi] = mapp[i][j][nowi-1];
}
}
}
int main(void)
{
int n=0;
mapp[1][1][1] = 'X';
for(int i=2; i<=7; i++)
{
int t = (int)pow(3.0,i-2);
copy1(1,1,t,i);
copy1(1,1+2*t,t,i);
copy1(1+t,1+t,t,i);
copy1(1+2*t,1,t,i);
copy1(1+2*t,1+2*t,t,i);
}
while(1)
{
scanf("%d", &n);
if(n==-1) break;
int t = (int)pow(3.0,n-1);
for(int i=1; i<=t; i++)
{
for(int j=1; j<=t; j++)
{
if(mapp[i][j][n]=='X')
printf("%c", mapp[i][j][n]);
else
printf(" ");
}
printf("\n");
}
printf("-\n");
}
return 0;
}
收获与反思
- 首先pow()里面的第一个元素应该是double类型, 否则某些编译系统(如poj)会CE. 当然正规一点的比赛应该没有这个问题(大概)
- 注意char类型数组未初始化时里面存的并不是空格, 需要输出空格时应该额外打印这些空格, 或直接存在原数组中
E. Cow Acrobats
大致题意
一群牛在叠罗汉, 他们分别用两个属性, 代表他们的重量和强壮度. 他们叠成竖直的一排, 下面的牛的危险系数是它上面所有牛的体重之和减去它的强壮度. 现在给这些牛排序使得这组牛当中最大的危险系数最小(最大对这组排序中所有牛而言, 最小对所有排序的可能而言)
分析
本质是一道贪心的题目, 需要得到(重量+强壮度越小的牛在越上面)这个结论, 题目就变得很简单了. 重点是由特例去思考整体的这种思想.
代码实现
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<limits.h>
using namespace std;
typedef long long ll;
const int MAXN = 50100;
int n;
typedef struct cow{
int w;
ll s;
ll sum;
}cow;
cow a[MAXN];
ll b[MAXN];
bool compare(const cow a1, const cow a2)
{
return a1.sum < a2.sum;
}
int main(void)
{
cin >> n;
for(int i=0; i<n; i++)
{
cin >> a[i].w >> a[i].s;
a[i].sum = a[i].w + a[i].s;
}
sort(a, a+n, compare);
for(int i=1; i<n; i++)
{
b[i] = a[i-1].w + b[i-1];
}
ll ans = INT_MIN;
for(int i=0; i<n; i++)
{
if(b[i]-a[i].s > ans) ans = (b[i]-a[i].s);
}
cout << ans << endl;
return 0;
}
收获与反思
虽说是一道思维题, 得到最后的结论好像就很简单了, 但在编写代码时还是有所收获的
- 使用sort()函数对结构体进行排序时(按照其中某一元素大小排序), 第一次使用到了sort()函数的第三个参数, 这段代码值得记录下来
bool compare(const cow a1, const cow a2)
{
return a1.sum < a2.sum;
}
sort(a, a+n, compare);
- 另外就是学习到了
<limits.h>
头文件中的INT_MIN
常量(对应的也有其他数据类型的max常量)用起来还是比较方便的(用来定义一个正无穷或负无穷)
F. To the Max
大致题意
给出一个N阶方阵, 求该方阵的一个子矩阵, 要求这个子矩阵里的所有元素和最大(这个子矩阵可以不是方阵, 也可以是它本身)
分析
最简单的一种思路, 是定下来起点的横纵坐标和终点的横纵坐标, 遍历它们, 得到元素和最大的那个子矩阵, 时间复杂度大约为O(n^ 4), 但是使用前缀和数组也可以快速通过(能过当然没啥问题, 而且终点坐标是根据起点坐标的位置来遍历的, 并不是严格的n^4, 还是挺快的)
另一种就是视频里所讲解的, 这次我们的前缀和数组里只存每一列自身的前缀和, 然后每次确定横坐标的起点和横坐标的终点, 再遍历在这样的横坐标限制下每一列的情况, 可以用前缀和快速得到答案
代码实现_1 (N^4)
#include<cstdio>
#include<limits.h>
int mapp[110][110];
int n;
void printmapp()
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
printf("%d ", mapp[i][j]);
}
printf("\n");
}
}
int answer(int x1, int y1, int x2, int y2)
{
int ans = 0;
for(int i=x1; i<=x2; i++)
{
for(int j=y1; j<=y2; j++)
{
ans += mapp[i][j];
}
}
return ans;
}
int main(void)
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d", &mapp[i][j]);
mapp[i][j] += mapp[i-1][j];
mapp[i][j] += mapp[i][j-1];
mapp[i][j] -= mapp[i-1][j-1];
}
}
// printmapp();
int max = INT_MIN;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
for(int k=i; k<=n; k++)
{
for(int l=j; l<=n; l++)
{
int ans = mapp[k][l]-mapp[k][j-1]-mapp[i-1][l]+mapp[i-1][j-1];
if(ans>max) max = ans;
}
}
}
}
printf("%d", max);
return 0;
}
代码实现_2 (N^3)
#include<cstdio>
#include<algorithm>
#include<limits.h>
using namespace std;
int mapp[110][110];
int n;
void printmapp()
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
printf("%d ", mapp[i][j]);
}
printf("\n");
}
}
int main(void)
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d", &mapp[i][j]);
mapp[i][j] += mapp[i-1][j];
}
}
// printf("\n");
// printmapp();
int ans = INT_MIN;
for(int i=1; i<=n; i++)
{
for(int j=i; j<=n; j++)
{
int last = 0;
for(int k=1; k<=n; k++)
{
last = last > 0 ? last : 0;
int number = mapp[j][k] - mapp[i-1][k];
ans = ans > last+number ? ans : last+number;
last = last + number;
}
}
}
printf("%d", ans);
return 0;
}
收获与反思
- 本题学习了如何储存二元数组的前缀和, 果然能优化很大的时间效率
- 一些题目其实有一个想法, 考虑好它的复杂度才是最重要的, 据说1s约等于10^8的时间复杂度, 所以大概是这个复杂度的想法可以直接实现它, 一些题目的算法其实并没有想象中那么难以实现
- 本题的N^3的算法中有一个思想个人认为较为关键, 就是第三重循环对每一列进行遍历时, 如果当前所有列的和是负数, 直接重新考虑后面的列的和是否比前面所保存的要大, 正是这种思想使得复杂度得到一个次方的优化.