棋盘覆盖
看到其他博客写的那么好
我觉得简单说一下思路就好(就是懒bushi:
我们从4* 4的棋盘看
将4* 4棋盘分成4份,每份都是2* 2(4个格子),任意一个格子为特殊点,那么剩下三个点自然而然就是L型骨牌的排放地,那么当前这四个格子就排满了。要想让剩下三份2* 2棋盘能被L型骨牌排满就让每一份2* 2棋盘都有一个特殊点,并且这三个特殊点还得连一块,所有最好的办法就是把除了已经有特殊点的剩下三份2* 2棋盘的相连点设为特殊点(划分依据)
比如
然后就是代码实现:
(tr,tc)是左上角点坐标(行,列),(dr,dc)是特殊点坐标(行,列),size是棋盘规格(size*size)
详细注释写在代码里了。
这边有一个需要注意的,就是如何填充L型骨牌,我用的是数字进行填充,一开始我是只设了chess全局变量,发现有问题,因为是将大的拆成小的,拆到不能拆为止,每次拆都要进入一个新的chessBoard函数(递归),chess都是++,对于新的棋盘没问题,每新一次拆分填充的颜色自然不同,但是当递归回溯,因为chess是全局变量,那么此时的chess就与开始递归时的chess不同了。
然后我就像把chess作为参数传入,也不行,对于不同子块填充L型数字是不同的,如果chess作为参数参与递归,那么当前棋盘进行划分,每一块新的棋盘的填充数字都是在当前棋盘的chess的基础上++,一个44的棋盘递归进入的所有22的棋盘的新的特殊点都是chess+1,错误。
所有我们既要做到每进入一个新的棋盘chess都要++,并且回溯后的chess还和开始递归的chess一样,那么就设一个变量nowchess存储开始回溯的chess,当前棋盘设置的L型骨牌都由nowchess填充。
#include<iostream>
using namespace std;
int board[20][20];
int chess = 0;
void chessBoard(int tr, int tc, int dr, int dc, int size) {
//左上角点坐标(行,列),特殊点坐标(行,列),棋盘规格size*size
if (size == 1)//当前规格为1*1,不能再划分了
return;
int len = size / 2;//行一分为2
chess++;
int nowchess = chess;//这一步处理重要!!
if (dr < tr + len && dc < tc + len)//特殊点在棋盘左上方
chessBoard(tr, tc, dr, dc, len);
else {//特殊点不在左上,左上方的右下角必作为新的特殊点
board[tr + len - 1][tc + len - 1] = nowchess;
chessBoard(tr, tc, tr + len - 1, tc + len - 1, len);
}
if (dr < tr + len && dc >= tc + len)//特殊点在棋盘右上方
chessBoard(tr, tc + len, dr, dc, len);
else {//特殊点不在右上,右上方的左下角必作为新的特殊点
board[tr + len - 1][tc + len] = nowchess;
chessBoard(tr, tc + len, tr + len - 1, tc + len, len);
}
if (dr >= tr + len && dc < tc + len)//特殊点在棋盘左下方
chessBoard(tr + len, tc, dr, dc, len);
else {//特殊点不在左下,左下方的右上角必作为新的特殊点
board[tr + len][tc + len - 1] = nowchess;
chessBoard(tr + len, tc, tr + len, tc + len - 1, len);
}
if (dr >= tr + len && dc >= tc + len)//特殊点在棋盘右下方
chessBoard(tr + len, tc + len, dr, dc, len);
else {//特殊点不在右下,右下方的左上角必作为新的特殊点
board[tr + len][tc + len] = nowchess;
chessBoard(tr + len, tc + len, tr + len, tc + len, len);
}
}
int main()
{
int size = 4;//棋盘规格为size*size
chessBoard(0, 0, 0, 0, size);
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++)
printf("%2d ", board[i][j]);
cout << endl << endl;
}
return 0;
}
最大子段和
#include<iostream>
using namespace std;
int maxsum(int str[], int beg, int end);//分治
int maxsum2(int str[], int beg, int end);//在线算法
int main()
{
//采用分治的思想,时间复杂度o(nlogn),在线算法是o(n)
int str[] = { -4,-5,-3,1,-10,-4,-5,-2 };
cout << maxsum(str, 0, 8) << endl;
cout << maxsum2(str, 0, 8);
return 0;
}
int maxsum2(int str[], int beg, int end) {
int sum = -0x3f3f3f3f, temp = 0;
for (int i = beg; i <= end; i++) {
temp += str[i];
sum = max(sum, temp);
if (temp < 0) temp = 0;
}
return sum;
}
int maxsum(int str[], int beg, int end)
{
if (beg == end) return str[beg];
//分治的本质也是递归,但凡递归一定要保证有递归结束条件
int mid = (beg + end) >> 1;
int sum1 = maxsum(str, beg, mid);
int sum2 = maxsum(str, mid + 1, end);
int tempsum = 0;
int leftsum = -0x3f3f3f3f;
//下面两个循环求得是穿过分界点的最大子序列和
for (int i = mid; i >= beg; i--)
{
tempsum += str[i];
leftsum = max(leftsum, tempsum);
}
tempsum = 0;
int rightsum = -0x3f3f3f3f;
for (int i = mid+1; i <= end; i++)
{
tempsum += str[i];
rightsum = max(rightsum, tempsum);
}
return max(leftsum + rightsum, max(sum1, sum2));
}
最近点对
(发布文章一直出错,原因竟然我标题里有最,我在线无语…
先用zui代替发出去再修改成最保存
麻了麻了真的麻了)
我觉得网上的解析写的挺好的,不想写了(hh就是懒
放个代码吧
因为没找到题目进行测试,所以不保证完全没问题,反正先扔着,以后有机会再修改(一般是没机会,估计我早忘了
没找到什么特别短的代码,以后有看到再修改吧(同上
理个简单的思路:
一块区间的所有点根据x进行排序,将所有点集分成两半,平分线x=m,保证两片区域的点尽可能相等
分别求两区间的最近点对距离d1,d2,d=min(d1,d2)
然后从刚刚的平分线x=m,往左到x=m-d,往右到x=m+d这一块区间[m-d,m+d]再找最近点对的距离d3,将d与d3进行比较,最小的那个距离就是我们想要的
采用的分治和递归(分治一般都递归
递归的返回条件(能计算当前区间的最近点对
两种情况:
1.当前区间只剩两个点,high-low==1,返回两点距离
2.当前区间三个点,a,b,c,那么最近点要么a-b,要么b-c,返回min(a-b,b-c)
#include<iostream>
#include<utility>
#include<queue>
#include<cmath>
#include<vector>
using namespace std;
typedef pair<double, double>PII;
double Closet(int low,int high,vector<PII>point);
double distance(int a,int b,vector<PII>p);
bool cmpy(PII a, PII b) {
return a.second < b.second;
}
bool cmpx(PII a, PII b) {
return a.first < b.first;
}
int main()
{
int n;
cin >> n;
vector<PII>point;
for (int i = 0; i < n; i++)//载入二维平面点的集合
{
double x, y;
cin >> x >> y;
point.push_back({ x,y });
}
//默认是根据x按顺序输入,如果不按顺序可以加个sort先排序
sort(point.begin(), point.end(), cmpx);
double d = Closet(0,point.size()-1,point);
cout << d;
return 0;
}
double Closet(int low,int high,vector<PII>point) {
double d1, d2, d3, d;
//low对于最左边点的左边,high对应最右边点的坐标
if (high - low == 1)
//只剩两个点了,这两个点的距离只能是当前区间的最近点距离
return distance(low,high,point);
else if (high - low == 2)
//三个点求最近对距离
{
d1 = distance(low, low + 1, point);
d2 = distance(low + 1, high, point);
//d3 = distance(low, high, point);
//不明白书上给的代码为什么还要比较最左边和最右边的,离谱
return min(d1,d2);
}
int mid = (high + low) / 2;//将一整片区间平分成两份
d1 = Closet(low, mid,point);
d2 = Closet(mid + 1, high,point);
d = min(d1, d2);
//算跨区间的点的最近对
vector<PII>span;
for (int i = mid; i >= low && point[mid].first - point[i].first <= d; i--)
span.push_back({ point[i].first,point[i].second });
for (int i = mid+1; i <= high && point[i].first - point[mid].first <= d; i++)
//这边注意一些i是从mid+1开始的,刚开始没注意就出问题了
span.push_back({ point[i].first,point[i].second });
//按照y进行排序
sort(span.begin(), span.end(), cmpy);
for(int i=0;i<span.size();i++)
for (int j = i + 1; j < span.size() && span[j].second - span[i].second <= d; j++) {
double t = distance(i, j, span);
d = min(d, t);
}
return d;
}
double distance(int a,int b,vector<PII>p) {
return sqrt(pow(p[a].first - p[b].first, 2) + pow(p[a].second - p[b].second, 2));
}