题意:
就是给你n个世界,每一个世界都有m个点,然后每个世界有一些有向边。当你在这个世界的时候,你要么停在这个点,要么走到可以走到的点。然后下一秒你会被传送到下一个世界,你所在点的ID不会变。现在让你选择一段连续的世界,然后可以从1号点走到m号点。问你最少选几个世界。如果没有答案输出-1。
思考:
1.刚开始我以为题意是,按顺序选择一些世界就行,不用连续。如果是这样的话,可以定义dp[i][j]为,用到了第i个世界,到j点的最小花费。这里要理清的是:不是在第i个世界中,到j点。而是在这前i个世界中到j点的最少选择几个世界。所以更新的时候,直接先从上一层更新最小值。然后再更新到达这个第i个世界右端点的最小花费。注意点在代码中注释了。
2.正确题意是连续的,那么就不能这样取最小花费了。如果在每个i点进行二分这样复杂度n×n×log(n)。那么就想dp。可以定义dp[i]为,所有能从1走到i中的下标最大的1的下标。然后更新的时候先更新maxn数组,看看这个世界的路径中可以到达哪些点。然后再更新一下dp,如果此时m点可达了,那么花费就是i-dp[m]+1。就是用到第i个世界的时候才可达,那么就看看最近的可以走过来的1是哪里。
代码:
可以不连续的代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e4+10,M = 2e3+10;
int T,n,m,k;
int va[N];
int dp[N][M]; //到第i个世界,到j点的最小花费
int minn[N];
vector<PII > v[N];
int solve()
{
mem(dp,0x3f);
for(int i=1;i<=n;i++)
{
dp[i][1] = min(dp[i][1],1ll); //到1的花费就是1
for(int j=1;j<=m;j++) dp[i][j] = min(dp[i][j],dp[i-1][j]); //到达j点的最小花费(不一定就是在这个世界中到达)
for(auto t:v[i])
{
if(t.fi==1) dp[i][t.fi] = dp[i][t.se] = 1; //特别注意的地方
else dp[i][t.se] = min(dp[i][t.se],dp[i-1][t.fi]+1); //如果在第i个世界到达t.se点,那么最小花费是多少。
}
}
if(dp[n][m]>1e9) return -1;
else return dp[n][m];
}
/*
由于每次只用上一层的,可以用滚动数组优化空间。
int solve()
{
mem(dp,0x3f);
for(int i=1;i<=n;i++)
{
dp[i&1][1] = min(dp[i&1][1],1ll);
for(int j=1;j<=m;j++) dp[i&1][j] = min(dp[i&1][j],dp[(i-1)&1][j]);
for(auto t:v[i])
{
if(t.fi==1) dp[i&1][t.fi] = dp[i&1][t.se] = 1;
else dp[i&1][t.se] = min(dp[i&1][t.se],dp[(i-1)&1][t.fi]+1);
}
}
if(dp[n][m]>1e9) return -1;
else return dp[n][m];
}
*/
signed main()
{
IOS;
cin>>n>>m;
for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) dp[i][j] = inf;
for(int i=0;i<=m;i++) minn[i] = inf;
for(int i=1;i<=n;i++)
{
cin>>k;
while(k--)
{
int a,b;
cin>>a>>b;
v[i].pb({a,b});
}
}
cout<<solve()<<"\n";
return 0;
}
必须连续的代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e4+10,M = 2e3+10;
int T,n,m,k;
int va[N];
int dp[M]; //能走到i的最大的1的下标。
int maxn[M];
vector<PII > v[N];
signed main()
{
IOS;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>k;
while(k--)
{
int a,b;
cin>>a>>b;
v[i].pb({a,b});
}
}
int minn = inf;
for(int i=1;i<=n;i++)
{
dp[1] = i; //每个世界都可以走到1
for(auto t:v[i]) maxn[t.se] = max(maxn[t.se],dp[t.fi]); //用上一层的dp更新maxn
for(int j=1;j<=m;j++) dp[j] = max(dp[j],maxn[j]); //再更新dp
if(dp[m]) minn = min(minn,i-dp[m]+1); //如果到第i个世界,可以走到m了,答案就是最近的1走到i的花费
}
if(minn==inf) cout<<-1;
else cout<<minn;
return 0;
}
总结:
多多思考,理清dp的状态定义。