1.小苯的迷宫行走

错误代码:
cpp
#include<iostream>
#include<queue>
#include<string.h>
#define int long long
using namespace std;
const int N = 4010;
int a[N][N];
int dp[N][N];
bool vst[N][N];
int n,m;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
typedef pair<int,int> PII;
int bfs()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
vst[i][j] = false;
dp[i][j] = 0;
}
queue<PII> q;
vst[1][1] = true;
q.push({1,1});
dp[1][1] = a[1][1];
while(q.size())
{
//开始bfs
auto t = q.front();q.pop();
int i = t.first,j = t.second;
for(int k=0;k<4;k++)
{
int x = i + dx[k],y = j + dy[k];
if(x>=1&&x<=n&&y>=1&&y<=m)
{
if(vst[x][y]==false)
{
//尚未访问过
vst[x][y] = true;
q.push({x,y});
}
dp[x][y] = max(dp[x][y],dp[i][j]|a[x][y]);
}
}
}
return dp[n][m];
}
signed main()
{
ios::sync_with_stdio();
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
cout<<bfs()<<'\n';
}
return 0;
}
代码注意点:
多组测试用例,要注意清空初始化的操作
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
vstij = false;
dpij = 0;
}
代码错误原因:
需要注意的是,动态规划需要满足无后效性,即后续的状态只能依赖当前状态继续往后,不能因为前面的状态修改了,后续的状态也跟着修改了
错误代码使用了bfs+动态规划的办法,假设结点一本来20的,对该节点的后续结点完成全部操作后,又被某一结点改为了30,那么最大值结果就乱套了
简单题简单做,可以通过标粗的地方看该题的解法。
- 面积为奇数的n*m矩阵,即 n*m = 奇数
- 走过的方格不能再走,则可以使用黑白染色规则来判断走到终点的步数奇偶性
证明:
color=(x+y)mod2,color = 0 为黑色(起点为黑色),color = 1 为白色
n*m = 奇数,则说明 n奇数m奇数,所以 n+m = 偶数,即 color = 0 为黑色
则需要奇数步才能达到终点,例如 黑->白->黑(起点也算1步,即 k = 1开始往后算)
则可得到哈密顿路径存在,可以遍历完整个棋盘,不会有点剩余下来
- 哈密顿路径:当起点终点颜色一致时,则代表可以遍历整个图,反之不可
或运算性质:越多数参加到或运算最后结果就越大,因为不管哪一位只要有个1,就恒定有1
根据该性质+哈密顿路径存在,得到把所有数参加到或运算,就能得到结果
ac代码:
cpp
#include<iostream>
#define int long long
using namespace std;
signed main()
{
int T;cin>>T;
while(T--)
{
int n,m;cin>>n>>m;
int ret = 0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int in;cin>>in;
ret|=in;
}
cout<<ret<<endl;
}
return 0;
}
2.小苯的好数

超时+错误代码:
cpp
#include<iostream>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];//统计每个数是否为好数
bool flag(int num)
{
//函数用来判断是否一个奇数的因子为奇数
int sum = 1;//1肯定是因子,且不参加到循环中
for(int i=2;i*i<=num;i++)
{
if(num%i==0)
{
//说明此时可以除尽
sum+=i;
int another = num/i;//除尽时,还有一个大的数可以作为因子
if(another!=i) sum+=another;
}
}
if(sum%2) return false;
else return true;
}
signed main()
{
int T;cin>>T;
while(T--)
{
int n,q;cin>>n>>q;
for(int i=1;i<=n;i++)
{
int in;cin>>in;
if(in%2==0)//为偶数时,必为好数
a[i] = 1;
else//为奇数时,只要因子和不为奇数,即为好数
if(flag(in)) a[i] = 1;
}
for(int i=1;i<=n;i++) a[i] = a[i] + a[i-1];
while(q--)
{
int l,r;cin>>l>>r;
cout<<(a[r]-a[l-1])<<endl;
}
}
return 0;
}
- 超时原因:flag判断最多要 1e3 次,最多要进入flag函数 2e5 次,6e8 次的时间开销肯定会超时
- 错误原因:没有初始化数组,上一次遗留的数组元素会影响到当前判断,所以要重新对数组进行初始化
- 解决办法:memset和isGood函数( O(1)判断完全平方数)
isGood函数如何得来?
当 x 为奇数时,只有完全平方数才能是好数。(完全平方数开完平方后依旧是一个整数的数字)
证明:
x 为奇数时,x = a*a 由于 a 必然是一个奇数(奇数*奇数才会为奇数),所以 a+1 为偶数
此时无论该完全平方数还能被拆分成什么数,都是两个奇数的状态,而 奇数+奇数 = 偶数
偶数 + 偶数 = 偶数 -> 偶数*奇数 = 好数
x为偶数时,由于无论怎么拆,都会多出来一个 1 没有数字和他配对,所以不能是好数
ac代码:
cpp
#include<iostream>
#include<cmath>
#include<cstring>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];//统计每个数是否为好数
bool isGood(int x)
{
int sq=sqrt(x);
return sq*sq==x;
}
signed main()
{
int T;cin>>T;
while(T--)
{
memset(a,0,sizeof(a));
int n,q;cin>>n>>q;
for(int i=1;i<=n;i++)
{
int in;cin>>in;
if(in%2==0)//为偶数时,必为好数
a[i] = 1;
else//为奇数时,只要因子和不为奇数,即为好数
if(isGood(in)) a[i] = 1;
}
for(int i=1;i<=n;i++) a[i] = a[i] + a[i-1];
while(q--)
{
int l,r;cin>>l>>r;
cout<<(a[r]-a[l-1])<<endl;
}
}
return 0;
}
3.小苯的ovo

什么是不相交?
ovo是长度 = 3 的连续三个字符,不相交 = 各个选中的ovo片段不能共用任何下标位置所以题目准确的表述是 不相交的ovo子串个数 ,看到题目的数据量不难想到用动态规划
- dpij:在前 i 个字符中找,获取 j 个 ovo 所需要的操作数量
- 不新增一个 ovo ,dpij = dpi-1j ;新增一个 ovo ,dpij = dpi-3j-1 + cost。此处的cost为从 i-3 -> i 如果要创造出一个ovo所需要的最少操作次数,这样天然解决了不相交的问题
- 初始化时,dp00 = 0,同时需要把无法填表的地方都设置为正无穷,要不然都为0的话就会出现问题
- 从左往右进行填表
- 返回结果时,遍历 dpnj 当中的 j ,第一次 j <= k 时表中存放的结果即为最终答案
ac代码:
cpp
#include<iostream>
#include<cstring>
using namespace std;
const int N = 5010,max_j = 1670;
int dp[N][max_j];
signed main()
{
int T;cin>>T;
while(T--)
{
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0][0] = 0;
int n,k;cin>>n>>k;
string s;cin>>s;
for(int i=1;i<=n;i++)
for(int j=0;j<=i/3;j++)
{
dp[i][j] = dp[i-1][j];
if(i>=3&&j>0)
{
int cost = (s[i-3]!='o')+(s[i-2]!='v')+(s[i-1]!='o');
dp[i][j] = min(dp[i][j],dp[i-3][j-1]+cost);
}
}
for(int j=n/3;j>=0;j--)
{
if(dp[n][j]<=k)
{
cout<<j<<endl;
break;
}
}
}
return 0;
}
4.红色和紫色

- 小红和紫都可以选择染成红色或紫色,而不是只能染一种颜色
- 对角线位置不算相邻的格子
cpp
#include<iostream>
using namespace std;
signed main()
{
int n,m;cin>>n>>m;
if((n*m)%2)cout<<"akai";
else cout<<"yukari";
return 0;
}
5.kotori和素因子

错误代码:
cpp
#include<iostream>
#define int long long
using namespace std;
const int N = 1010;
bool vst[N];//判断是否可以拆分为一个质因子
int n;
void deprime(int in)
{
while(in%2==0)
{
vst[2] = true;
in /= 2;
}
for(int i=3;i*i<=in;i+=2)
{
while(in%i==0)
{
vst[i] = true;
in/=i;
}
}
if(in>1)
vst[in] = true;
}
signed main()
{
cin>>n;
int mx = 2;
for(int i=1;i<=n;i++)
{
int in;cin>>in;
deprime(in);//获取其质因子
mx = max(mx,in);
}
int cur = n,ret = 0;
for(int i=2;i<=mx;i++)
{
if(!cur) break;
if(vst[i])
{
ret+=i;
cur--;
}
}
if(cur) cout<<-1;
else cout<<ret;
return 0;
}
- 错误原因:没考虑到 a、b 两个因子可能不重合,然后 a 的两个因子被统计到结果里了,但 b 的因子却没有
所以,我们可以通过dfs的方式做这道题
打表+dfs
ac代码:
cpp
#include<iostream>
using namespace std;
const int N = 20,max_num = 1010;
int ret = 0x3f3f3f3f;
int a[N],prime[max_num],pos;//prime数组打表,获取1000以下的所有质数
int n;
bool vst[max_num];//统计哪些质数已经被用过了
int path;//统计某一情况下可以获得的最大值
bool flag(int x)
{
for(int i=2;i*i<=x;i++)
if(x%i==0) return false;
return true;
}
void init()
{
for(int i=2;i<=max_num;i++)
{
if(flag(i)) prime[++pos] = i;//flag函数用来判断是否为素数,如果是素数那么就放入prime表
}
}
void dfs(int pos)
{
if(pos==n+1)
{
ret = min(ret,path);
return;
}
int cur = a[pos];
//一个个试过去
for(int i=1;prime[i]<=cur;i++)
{
if(!vst[i])
{
int p = prime[i];
if(cur%p==0)
{
//说明p是cur的一个因子,即该素数可以整除数组当前数
vst[i] = true;
path+=p;
dfs(pos+1);
path-=prime[i];
vst[i]=false;
}
}
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
init();
dfs(1);
cout<<(ret==0x3f3f3f3f?-1:ret);
return 0;
}