结果填空:马的管辖
题目链接:https://nanti.jisuanke.com/t/A2236
题解:官方给的是二进制枚举,但是我没有看明白^菜^,先贴在这,慢慢体会。
#include <cstdio>
#include <cstring>
using namespace std;
int px[4] = {0, 0, 1, -1};
int py[4] = {1, -1, 0, 0};
int dx[4][2] = {-1, 1, -1, 1, 2, 2, -2, -2};
int dy[4][2] = {2, 2, -2, -2, -1, 1, -1, 1};
int vis[5][5], g[5][5];
int n = 5, m = 5;
bool in(int x, int y) { return x >= 0 && x < n && y >= 0 && y < m; }
int main() {
int r = n * m;
int minx = r + r, ans = 0;
for (int s = 0; s < 1 << r; s++) {
memset(vis, 0, sizeof(vis));
int cnt = 0;
for (int i = 0; i < r; i++) {
if (s >> i & 1) {
g[i / m][i % m] = 1;
cnt++;
} else {
g[i / m][i % m] = 0;
}
}
if (cnt > minx) continue;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (g[i][j] == 0) continue;
vis[i][j] = 1;
for (int k = 0; k < 4; k++) {
int x = i + px[k], y = j + py[k];
if (in(x, y) && g[x][y] == 0) {
for (int u = 0; u < 2; u++) {
int tx = i + dx[k][u];
int ty = j + dy[k][u];
if (in(tx, ty)) {
vis[tx][ty] = 1;
}
}
}
}
}
}
int ok = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (vis[i][j] == 0) {
ok = 0;
break;
}
}
}
if (ok) {
if (cnt < minx) {
minx = cnt;
ans = 1;
} else if (cnt == minx) {
ans++;
}
}
}
printf("%d %d\n", minx, ans);
return 0;
}
代码填空:LIS
题目链接:https://nanti.jisuanke.com/t/A2237
题解:LIS有两种方法,这个地方考察的是二分操作,时间复杂度为O(nlogn)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 9;
int f[N], a[N];
int n;
int find(int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (f[mid] < x) {
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
int lis() {
int len = 0;
for (int i = 0; i < n; i++) {
int k = find(0, len, a[i]);
f[k] = a[i];
if (k == len) {
len++;
}
}
return len;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", a + i);
}
printf("%d\n", lis());
return 0;
}
程序设计:找质数
题目链接:https://nanti.jisuanke.com/t/A2238
题解:水题,欧式筛法和二分查找的基本操作
#include<bits/stdc++.h>
const int MAXN = 1000010;
using namespace std;
int is_prime[MAXN];
int primes[MAXN];
int a[MAXN];
int cnt=0;
//欧式筛法
void Euler(){
is_prime[1]=0;
for(int i=2;i<MAXN;i++){
if(is_prime[i])
primes[cnt++]=i;
for(int j=0;j<cnt&&i*primes[j]<=MAXN;j++){
is_prime[i*primes[j]]=0;
if(i%primes[j]==0)
break;
}
}
}
//二分,也可以用lower_bound
int BinarySearch(int l, int r, int k){
int mid;
while(l<r){
mid=(r-l)/2+l;
if(primes[mid]<k)
l=mid+1;
else
r=mid;
}
return mid;
}
int main()
{
memset(is_prime,1,sizeof(is_prime));
memset(primes,0,sizeof(primes));
Euler();
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++){
int r = BinarySearch(0, cnt-1, a[i]);
int l = 0;
while(l<=r){
if(primes[l]+primes[r]==a[i]){
cout<<primes[l]<<" "<<primes[r]<<"\n";
break;
}
else if(primes[l]+primes[r]>a[i])
r--;
else
l++;
}
}
return 0;
}
程序设计:后缀字符串
题目链接:https://nanti.jisuanke.com/t/A2239
题解:思维题,用到了map,将每个字符串的n到1长度的后缀字符串加入map,并计数,直接用substr截取后缀字符串。
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
int main() {
int n;
cin>>n;
string st;
vector<string> s;
map<string, int> record;
for(int i=0;i<n;i++){
cin>>st;
s.push_back(st);
for(int j=0;j<st.size();j++)
record[st.substr(j)]++;
}
for(vector<string>::iterator iter=s.begin();iter!=s.end();iter++)
cout<<record[*iter]<<"\n";
return 0;
}
程序设计:轻重搭配
题目链接:https://nanti.jisuanke.com/t/A2240
题解:贪心问题,先预处理从小到大排列,然后分成两部分,前一部分依次和后一部分大于等于两倍的匹配,然后注意无法匹配时指针偏移情况就行了。
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
const int MAXN=5*1e5+5;
using namespace std;
int a[MAXN];
int main() {
int n;
int res=0;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);
int l=0,r=n/2;
while(r<n){
if(a[r]/a[l]>=2){
res++;
l++;r++;
}else{
res++;
r++;
}
}
while(l<n/2){
res++;
l++;
}
cout<<res<<"\n";
}
程序设计:抠图
题目链接:https://nanti.jisuanke.com/t/A2230
题解:这道题DFS和BFS都可以做,首先只保留黑色线框里面的颜色,即存在0环且其内的颜色不变,这道题的用例全是给的0和1,实际上是0到225,是个坑点。为了让0环内的颜色被保护,所以试想不能被0环保护的颜色一定与图的边框相连,所以这道题就可以转化为一个连通分量的问题,我们将图边框上大于0的像素点进入队列,然后将将其置0,然后将依次出队的大于0的像素点进行DFS,将它周围大于0的全部置为0,最后只有0环中的像素点不会受到影响。
#include<bits/stdc++.h>
const int MAXN = 100010;
using namespace std;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int mat[505][505];
int t, n, m;
void dfs(int i, int j){
for(int d=0;d<4;d++){
int new_i = i+dx[d];
int new_j = j+dy[d];
if(new_i>=0&&new_i<=n-1&&new_j>=0&&new_j<=m-1&&mat[new_i][new_j]>0){
mat[new_i][new_j]=0;
dfs(new_i, new_j);
}
}
}
int main()
{
cin>>t;
while(t--){
queue<pair<int, int> > qu;
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>mat[i][j];
if(i==0||i==n-1||j==0||j==m-1){
if(mat[i][j]>0){
qu.push(make_pair(i, j));
mat[i][j]=0;
}
}
}
}
while(!qu.empty()){
dfs(qu.front().first, qu.front().second);
qu.pop();
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cout<<mat[i][j];
if(j==m-1)
cout<<"\n";
else
cout<<" ";
}
}
}
return 0;
}
程序设计:蒜厂年会
题目链接:https://nanti.jisuanke.com/t/A2232
题解:这道题和51Nod上的1050 循环数组最大子段和是一样的,不过这道题我是按照官方的做法,用队列来做,破环为链,也就是做将N长度的环转化为2*N的链,然后求前缀和,那么前缀和之差就可以表示那一段的连续之和,这里队列实际上是一个双端队列,维护一个单调队列,让队列元素从小到大,维护一个每次进队的元素和队头元素之差的最大值即可,然后还要判断的是队列中队头元素的索引要小于n-i。
#include<bits/stdc++.h>
#define ll long long
const int MAXN = 2e5+10;
using namespace std;
int a[MAXN];
ll pre[MAXN];
int main()
{
deque<int> deq;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
pre[0]=0;
for(int i=1;i<=2*n;i++)
pre[i]=pre[i-1]+a[i];
deq.push_back(0);
ll res=a[1];
for(int i=1;i<=2*n;i++){
while(!deq.empty() && deq.front() <i-n)
deq.pop_front();
res = max(res, pre[i]-pre[deq.front()]);
while(!deq.empty() && pre[deq.back()]>=pre[i])
deq.pop_back();
deq.push_back(i);
}
cout<<res<<"\n";
return 0;
}