D
暴力+贪心
由于 n <= 1000,可以考虑
n
2
n^2
n2 的做法,枚举每个位置能到达的所有位置,bfs 即可。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 1e3+3;
using ll = long long;
int a[maxn],vis[maxn];
int dis[maxn];
void solve()
{
int n; cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
queue<pair<int,int>> q;
int x,y;
memset(dis,0x3f,sizeof(dis));
dis[1]=0,vis[1]=1; // 1 位置的初始步数为 0
q.emplace(0,1); // 初始步数,初始位置
while(!q.empty())
{
x=q.front().first;
y=q.front().second;
q.pop();
for(int i=1;i<=n;i++)
{
if(i==y) continue; // 枚举到当前位置,直接跳过
// 如果说这个点在前边被访问过,那么从前边访问一定比从后面再访问更优,所以不需要访问了
if(a[y]%abs(y-i)==0&&!vis[i])
{
dis[i]=x+1;
vis[i]=1;
q.emplace(x+1,i);
}
}
}
cout<<dis[n]<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
E
每块布告有选或不选两个选项,01背包。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
using ll = long long;
const int maxn = 5003;
int a[maxn],dp[maxn];
void solve()
{
int n; cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
memset(dp,0x3f,sizeof(dp));
dp[0]=0; // 初始值,供状态转移
for(int i=1;i<=n;i++) // 枚举每块布告
{
for(int j=n;j>=a[i];j--) // 每块布告只能选一次,倒着枚举墙的面积
if(j>=i&&j<=i+a[i]-1) // 枚举每块布告能覆盖到的范围
dp[j]=min(dp[j-a[i]]+1,dp[j]);
}
if(dp[n]>n) dp[n]=-1;
cout<<dp[n]<<endl;
return;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; cin>>T;
while(T--) solve();
return 0;
}