acwing2716
看到结果可能取很大的值,就可以猜到要用DP。关键在于使用dp的话要按照什么顺序。题目使用的是有向图且食物链有先后顺序,所以联想到拓扑排序,使用拓扑排序得到拓扑序列,从而进行dp。dp[i]表示当前以i为结尾的食物链的条数(此时假设i没有出边)。假设节点i的后继是节点j,那么处理i的时候要对节点的出度减1,并且,更新dp[j]+=dp[i];对于入度为0且出度不为0的节点,初始化其dp值为1。拓扑排序完毕后,即可得到结果。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAX = 100005;
//点数,边数
int n, m;
//邻接表,节点编号从1开始
vector<int> adj[MAX];
//dp数组,储存当前以该节点为末尾的食物链的个数
int dp[MAX] = { 0 };
//节点的入度、出度
int ind[MAX] = { 0 };
int outd[MAX] = { 0 };
//判断节点是否进入过队列
bool inq[MAX] = { false };
//总数
int totalnum = 0;
//读输入,构造邻接表和ind,outd数组
void input() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
outd[u]++;
ind[v]++;
}
}
//通过拓扑排序的顺序,进行DP
void toposort() {
//队列,储存节点编号
queue<int> q;
//将入度为0且出度不为0的节点的dp值设为1,并push入队列
for (int i = 0; i < n; i++) {
if (ind[i] == 0 && outd[i] != 0) {
dp[i] = 1;
q.push(i);
}
}
//循环,找拓扑序列
while (!q.empty()) {
int cid=q.front();
q.pop();
//删除出边
for (int i = 0; i < adj[cid].size(); i++) {
int u = adj[cid][i];
ind[u]--;
//修改u的dp值
dp[u] += dp[cid];
//将新增的入度为0的点加入队列,并更新最终结果
if (ind[u] == 0) {
q.push(u);
if (outd[u] == 0)
totalnum += dp[u];
}
}
}
}
int main(void) {
input();
toposort();
cout << totalnum << endl;
}

浙公网安备 33010602011771号