暴力求解方法:
逐块检查;
关键:字符串处理;矩阵的存入和读取;
字符串处理:
基于< string >:
- (append)string cur = "";如果在字符串后面拼接上什么>东西:cur+="[str]";或者cur.append("[str]");
- (length)cur.size();/cur.length();
- (substr)cur.substr(pos,len);从pos开始,取len个字符;
- (find)cur.find("abc");//从前往后 cur.rfind("abc");从后往前;
- (replace)cur.replace(pos,len,"new");
- (insert/erase)cur.insert(pos,"new");// cur.erase(pos,len);
- 大小写转换:使用
; std::transform(s.begin(),s.end(),::tolower);//或者::toupper; - 字符访问:s[i]/s.at(i)
- 转c字符串:s.c_str();在printf的时候使用
基于< cstring >:
多出拷贝,比较等;
矩阵的存入和读取
vector<vector
vector<vector
vector<vector
a.resize(n, vector
暴力求解时间复杂度为\(O(L\times n^2)\)
代码如下:
#include<iostream>
#include<vector>
#include<string>using namespace std;
int n,L;vector<string> transform(vector<vector<int>> mat,int k,int n){vector<string>res(n);for(int i = 0;i < n;i++){string cur = "";for(int j = 0;j < n;j++){if(mat[i][j] >= k)cur+="1";else cur+="0";}res[i] = cur;// cout << cur<<endl;}return res;
}
//在x到x+4行之间逐行进行模式匹配,9个字符串为一组作为匹配单位
bool compare(vector<string> str,vector<string> target,int x){int num = str[x].size();for(int i = 0;i <= num-9;i++){bool ans = true;for(int j = 0; j <= 4;j++){string sub = str[x+j].substr(i,9);//截取该段从i开始往后9个位置if(target[j] != sub){ans = false;break;}}if(ans)return true;}return false;
}int main(){cin >> n >> L;vector<string> target = {"111111111","100100101","100111110","100001100","111111100"};//输入图片vector<vector<int>> mat(n,vector<int>(n));//nxn矩阵for(int i = 0; i<n;i++){for(int j = 0;j < n;j++){cin >> mat[i][j];}}//暴力遍历对比,k属于0-L-1for(int k = 0; k < L; k++){vector<string> strmat = transform(mat,k,n);for(int x = 0; x <= n-5;x++){bool cur = compare(strmat,target,x);if(cur){cout << k <<endl;break;}}}
}
非暴力,寻找可行解:
我们可以知道,对于每一个5x9矩阵,由于具有mat[i][j] >=k才为1,mat[i][j]<=k才为0的特性,因此我们实际上可以得到一个线性规划问题,有5x9个方程,我们把在上下界中的全部解都存入答案中即可找到所有k;
这样就把暴力遍历转为求解问题,时间复杂度变为\(O(n^2)\);
代码:
#include<iostream>
#include<vector>
#include<string>
#include<set>using namespace std;
int n,L;
set<int> res;int main(){cin >> n >> L;vector<vector<int>> target = {{1,1,1,1,1,1,1,1,1},{1,0,0,1,0,0,1,0,1},{1,0,0,1,1,1,1,1,0},{1,0,0,0,0,1,1,0,0},{1,1,1,1,1,1,1,0,0}};//输入图片vector<vector<int>> mat(n,vector<int>(n));//nxn矩阵for(int i = 0; i<n;i++){for(int j = 0;j < n;j++){cin >> mat[i][j];}}//[i,j]是这个矩阵的第一个元素所在的地块,不需要进行二值化了for(int i = 0;i <= n-5;i++){for(int j = 0; j <= n-9;j++){int k_up = L;int k_low = 0;for(int x = 0; x <= 4;x++){for(int y = 0; y <= 8;y++){if(target[x][y] == 1){k_up = min(k_up,mat[i+x][j+y]);}else{k_low = max(k_low,mat[i+x][j+y]);}}}for(int s = k_low+1;s <= k_up;s++){res.insert(s);}}}for(auto e : res){cout << e << endl;}
}
这是我自己仿照写的,会导致TLE一个数据点
差分的思路:
差分做法关键:
diff[L] += 1
diff[R+1] -= 1
表示:从L开始可以,到R+1这里停止;因此加 1 的区间是[L,R];
需要还原的时候,比如说s[i]就等于:\(num(系数)*\sum_{i=0}^{i}diff[i]\)
因此关键改动在:
vector<int> diff(L+3);//[i,j]是这个矩阵的第一个元素所在的地块,不需要进行二值化了for(int i = 0;i <= n-5;i++){for(int j = 0; j <= n-9;j++){int k_up = L;int k_low = 0;for(int x = 0; x <= 4;x++){for(int y = 0; y <= 8;y++){if(target[x][y] == 1){k_up = min(k_up,mat[i+x][j+y]);}else{k_low = max(k_low,mat[i+x][j+y]);}}}if(k_up >= k_low+1){diff[k_low+1]+=1;//从diff[k_up+1]-=1;}}}for(int i = 0; i <= L; i++){diff[i] += diff[i-1]; // 恢复真实次数,是否此处被允许要考虑到前面是否存在区间包含它,如果存在那么就允许if(diff[i] > 0){cout << i << endl;}}