8/3 星期一 晴
今天又是dp的一天,果然dp的思想还是不能一下子就能理解,关于状态转移方程和决策这方面虽然说我能够看懂,但是还是需要更深层次的去探究。书上面的内容比较难懂啊,需要更多得去吃透。
不过今天做了一道dp得题目,思想还是比较容易 洛谷P2196 挖地雷
思路:最终是要求出最多的地雷数,并且还要回溯找到路线。之前看LIS问题的时候看见一个大佬的博客,上面有关于回溯的方法。所以我在做这题的时候就用了这个
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=25;
int pre[maxn],dp[maxn]={0},road[maxn][maxn],a[maxn],k=0,n,pos,Max=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],dp[i]=a[i],pre[i]=i;
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++)
cin>>road[i][j];
///找到挖最多地雷地窖的路,用dp去找出线路
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(road[i][j]) ///如果更够走到该地窖
{
dp[j]=max(dp[j],dp[i]+a[j]);
if(Max<dp[j]) Max=dp[j];
if(dp[j]==dp[i]+a[j]) ///更新地窖位置,pre数组用来装入之前地窖的位置
{
pos=j;
pre[j]=i;
}
}
else if(Max<dp[j]) ///如果不能走则判断该地窖的地雷数是否大于Max
pos=j,Max=dp[j];
}
}
Max=0;
for(int i=1;i<=n;i++)
if(dp[i]>Max)
{
pos=i;
Max=dp[i];
}
a[k++]=pos;
for(int i=pos;pre[i]!=i;i=pre[i])
a[k++]=pre[i];
for(int i=k-1;i>=0;i--)
cout<<a[i]<<" ";
cout<<endl;
cout<<dp[pos];
return 0;
}
PS:其中在进行状态转移方程的时候,还是有问题的(关于回溯这方面用的还是太少了)。整体方面题目不是很难,但是在能否走通下一个地窖这里,我没有考虑周全。比如有一个地窖什么都不通但是地雷数却非常大,那么就得选这个地窖,所以还是得多加思考
8/4 星期二 晴
动态规划果然还是需要很多前置知识的,看到后面很多地方都需要一些前置知识。比较难去看懂算法书而且题目一点思路都没有,转头去写搜索dfs题单去了。关于dfs问题,还是得先去看它的特点 今天做了一道题 洛谷P1135奇怪的电梯
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200+5;
int a[N],n,A,B,flag=0,vis[N];
long long cnt=1e+9;
int dfs(int floor,long long sum)
{
if(sum>ans) return; ///TLE
if(floor==B) {cnt=min(cnt,sum);flag=1;return 0;}
vis[floor]=1; ///表示此楼层已经到达过
if(floor+a[floor]<=n&&!vis[floor+a[floor]]) ///如果电梯不会回到原来的层数
dfs(floor+a[floor],sum+1);
if(floor-a[floor]>=1&&!vis[floor-a[floor]]) ///同理
dfs(floor-a[floor],sum+1);
vis[floor]=0; ///回溯
}
int main()
{
scanf("%d%d%d",&n,&A,&B);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dfs(A,0);
if(flag)
cout<<cnt;
else
cout<<-1;
return 0;
}
PS:题目的难度不难,TLE的点以及电梯会不会回到原来的楼层是一个主要解决的问题。还是得多去思考一下!
8/6 星期四 晴
还是在学习dfs,感觉做其他题目的时候都需要dfs搜索,所以今天做了之前看了但是没有思路的题目 洛谷P1036 选数
主要就是素数的判别还有这题对选数的顺序要求不高。
#include<iostream>
#include<cmath>
using namespace std;
int n,k,a[25],cnt=0,vis[25];
bool is_prime(int t) ///判断素数
{
if(t==1||t==0) return false;
if(t==2) return true;
for(int i=2;i<=sqrt(abs(t));i++)
if(t%i==0)
return false;
return true;
}
void dfs(int now,int x,int sum)
{
if(x==k)
{
if(is_prime(sum))
cnt++;
}
else
{
for(int i=now;i<=n;i++)
{
if(vis[i]==1) continue; ///如果已经被选中,跳过此数
vis[i]=1;
dfs(i+1,x+1,sum+a[i]); ///选择此数
vis[i]=0; ///回溯,不选择此数
}
}
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
dfs(1,0,0);
cout<<cnt;
return 0;
}
8/7 星期五 晴
今天做了一道类似于跳马的题目,一直再调试数据改BUG,还是在dfs时候如何防止爆栈出了问题,还是得注意栈的问题。从找每次跳马所需的最小值到如何防止爆栈这期间,真的是头皮发麻。还是得注意爆栈的问题,要有好的限制条件才行!!以后要注意这些
洛谷P1443 马的遍历
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=405;
int N,M,sx,sy,vis[maxn][maxn];
void dfs(int x,int y,int step)
{
if(x==sx&&y==sy&&step!=1)
return ;
if(step>200) return ;
if(x-2>=1&&y-1>=1)
{
if(vis[x-2][y-1]==-1||vis[x-2][y-1]>step) ///我想到了这一点,但是不知道怎么去写
{
vis[x-2][y-1]=step;
dfs(x-2,y-1,step+1);
}
}/// x-2,y-1
if(x-1>=1&&y-2>=1)
{
if(vis[x-1][y-2]==-1||vis[x-1][y-2]>step)
{
vis[x-1][y-2]=step;
dfs(x-1,y-2,step+1);
}
}/// x-1,y-2
if(x-2>=1&&y+1<=M)
{
if(vis[x-2][y+1]==-1||vis[x-2][y+1]>step)
{
vis[x-2][y+1]=step;
dfs(x-2,y+1,step+1);
}
}/// x-2,y+1
if(x-1>=1&&y+2<=M)
{
if(vis[x-1][y+2]==-1||vis[x-1][y+2]>step)
{
vis[x-1][y+2]=step;
dfs(x-1,y+2,step+1);
}
}/// x-1,y+2
if(x+2<=N&&y-1>=1)
{
if(vis[x+2][y-1]==-1||vis[x+2][y-1]>step)
{
vis[x+2][y-1]=step;
dfs(x+2,y-1,step+1);
}
}/// x+2,y-1
if(x+1<=N&&y-2>=1)
{
if(vis[x+1][y-2]==-1||vis[x+1][y-2]>step)
{
vis[x+1][y-2]=step;
dfs(x+1,y-2,step+1);
}
}/// x+1,y-2
if(x+2<=N&&y+1<=M)
{
if(vis[x+2][y+1]==-1||vis[x+2][y+1]>step)
{
vis[x+2][y+1]=step;
dfs(x+2,y+1,step+1);}
}/// x+2,y+1
if(x+1<=N&&y+2<=M)
{
if(vis[x+1][y+2]==-1||vis[x+1][y+2]>step)
{
vis[x+1][y+2]=step;
dfs(x+1,y+2,step+1);
}
}/// x+1,y+2
}
int main()
{
scanf("%d%d%d%d",&N,&M,&sx,&sy);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
vis[i][j]=-1;
vis[sx][sy]=0;
dfs(sx,sy,1);
for(int i=1;i<=N;i++)
{
for(int j=1;j<=M;j++)
printf("%-5d",vis[i][j]);
cout<<endl;
}
return 0;
}
今天还做了一道迷宫,之前在B站看视频的时候看到了关于迷宫dfs问题。我觉得很有意思然后在洛谷里面找了迷宫题刷了一下,有了这种思想果然还是挺简单的,但是自己还是犯了一个错误
洛谷P1605
#include<iostream>
using namespace std;
int N,M,n,vis[6][6]={0},ban[6][6]={0},sx,sy,fx,fy,sum=0;
void dfs(int x,int y)
{
if(x==fx&&y==fy)
{
sum++; return ;
}
if(vis[x][y+1]==0&&ban[x][y+1]!=1&&y+1<=M) ///向右搜索路径
{
vis[x][y+1]=1;
dfs(x,y+1);
vis[x][y+1]=0; ///回溯
}
if(vis[x+1][y]==0&&ban[x+1][y]!=1&&x+1<=N) ///向下
{
vis[x+1][y]=1;
dfs(x+1,y);
vis[x+1][y]=0;
}
if(vis[x][y-1]==0&&ban[x][y-1]!=1&&y-1>=1) ///向左
{
vis[x][y-1]=1;
dfs(x,y-1);
vis[x][y-1]=0;
}
if(vis[x-1][y]==0&&ban[x-1][y]!=1&&x-1>=1) ///向上
{
vis[x-1][y]=1;
dfs(x-1,y);
vis[x-1][y]=0;
}
return ;
}
int main()
{
cin>>N>>M>>n;
cin>>sx>>sy>>fx>>fy;
for(int i=1;i<=n;i++)
{
int t,f;
cin>>t>>f;
ban[t][f]=1;
}
vis[sx][sy]=1; ///因为这里没有加所以wa了,这种错误不能再犯了
dfs(sx,sy);
cout<<sum;
}
PS:关于递归dfs这类问题,一定要注意爆栈和TLE的问题。限制条件也得认真去想,而不是一股脑的去写。我得更加多学习和改正!