一个完全图(即任意两点之间都有一条双向道路),给定 m m m条废弃的道路,问从 1 1 1号城市出发走 k k k步走回 1 1 1号的方案有多少种。
观察数据范围
n
<
=
5000
n <= 5000
n<=5000
m
<
=
5000
m <= 5000
m<=5000
k
<
=
5000
k<=5000
k<=5000可以考虑二维暴力DP 。
定义DP数组
f
[
i
]
[
j
]
f[i][j]
f[i][j] : 第
i
i
i步走到城市
j
j
j的方案数
对于每一步枚举从城市
k
k
k出发,可以得到dp方程
f
[
i
]
[
j
]
=
∑
k
=
1
n
f
[
i
−
1
]
[
k
]
f[i][j] = \sum_{k=1}^{n}f[i - 1][k]
f[i][j]=∑k=1nf[i−1][k](城市
k
k
k到城市
j
j
j的道路存在且还没有废弃)
估计时间复杂度 因为枚举步数,出发城市,存在的道路都是O(n)的 循环嵌套时间复杂度为
O
(
n
3
)
O(n^3)
O(n3) 显然不足以通过此题。
考虑如何优化,因为dp方程的定义已经确定,所以只能从枚举每个城市连接的道路着手。
时间复杂度在于,因为是完全图删去的道路最多为
5000
5000
5000条,所以对于每个城市直接连接的城市仍然有很多。
正难则反,因为删去的道路很少,我们可以枚举出发城市
k
k
k所不能到达的城市,而不是能到达的城市。
在前一次dp求解中记录下
s
u
m
=
∑
j
=
1
n
f
[
i
−
1
]
[
j
]
sum = \sum_{j=1}^{n}f[i - 1][j]
sum=∑j=1nf[i−1][j] 在枚举城市
k
k
k时
s
u
b
=
∑
k
=
1
n
f
[
i
−
1
]
[
j
]
sub = \sum_{k=1}^{n}f[i - 1][j]
sub=∑k=1nf[i−1][j](
k
k
k不能到达
j
j
j)
那么答案就是
f
[
i
]
[
j
]
=
∑
k
=
1
n
f
[
i
−
1
]
[
k
]
f[i][j] = \sum_{k=1}^{n}f[i - 1][k]
f[i][j]=∑k=1nf[i−1][k](城市
k
k
k到城市
j
j
j的道路存在且还没有废弃)
=
s
u
m
−
s
u
b
= sum - sub
=sum−sub
因为删除的道路很少,在每次循环中遍历的次数之和恰好为
m
∗
2
m * 2
m∗2
时间复杂度优化到
O
(
n
∗
m
)
O(n * m)
O(n∗m)
#include
#include
#include
using namespace std;
#define ll long long
const int N = 5010, mod = 998244353;
int n,m,k;
int f[N][N];//第i步走到j地的方案
vector<int>g[N];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= m; i ++){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
f[0][1] = 1;
ll sum = 1;//f[i - 1]的道路方案数总和
for(int i = 1; i <= k; i ++)
{
ll tmp = 0;
for(int j = 1; j <= n; j ++)
{
ll sub = f[i - 1][j];//道路废弃的城市方案数减去,初始化为f[i-1][j]因为自己不能到自己
for(int v : g[j]){
sub = (sub + f[i - 1][v]) % mod;
}
f[i][j] = (sum + mod - sub) % mod;
tmp = (tmp + f[i][j]) % mod;//记录sum
}
sum = tmp;
}
printf("%d\n",f[k][1]);
return 0;
}
更新:
E:2022.11.4
F待补