目录
题意:
有向图。
从1走到n,可以停一天去封某条路。返回 最优地封完后的 最坏情况。
样例3:
思路:
https://www.cnblogs.com/AWhiteWall/p/13158089.html(参考佬的思路)
方法:
比如到 n点 有 3条路,但是剩下两条比最优的一条要远十几天。那么不如用两天时间把这两条路给封掉。(这是正向想的)
所以干脆算最优时直接加上两天 , 取这几种的最小值即可。(加的这两天就是 n 点的“剩余入度”)
——————
所以我们反向建有向图,反向dijktra,每条路的距离(天数)为 1 加上 到此点的剩余度数。
答疑:
1.为什么反向做呢?
因为我们不要离谱长的边,要直接封掉,那么这些路后面的路都不考虑了。这是“趋近于结果的”贪心。
2.为什么是到达点的剩余度数呢?
我们dijkstra是收集的每个点到起始点( n点 )最短路。 每次操作也是用的当前有的最短路。
本次操作是 n 到达这个点 v 的最短路,也是 v 的最优,结合我们的思路,取最坏(封掉其他路),要加上剩余度数。
3.相同路是否可以去重,用个set?
不可以。因为对于最优解,可能要让这些路都封掉。
4.如果有多条路相同呢?
没关系。等到多条中的最后一条时,这条减的度数最小,也是这种路的最优解。(这几条路不是同一“时空”)
参考代码:
发现我的dijkstra板子感觉可以简写~~(可以看看官方题解的简洁代码)
#define endl "\n"
//#define int long long
//不能用set,因为那些长的重复的都要删!!
const int maxn = 2e5 + 5;
int arr[maxn];
int d[maxn];
int tmpdis = 1;
struct dis_node//放堆里面比长度,但是想知道端点
{
int dis;
int next;
bool operator < (const dis_node& a)
{
return dis < a.dis;
}
dis_node(int d, int n)
{
dis = d; next = n;
}
};
class cmp
{
public:
bool operator()(dis_node a, dis_node b)
{
return a.dis > b.dis;//
}
};
void dijkstr(vector<int>& dij, vector<vector<int>>& arr, int ori, int n)
{
priority_queue<dis_node, vector<dis_node>, cmp>heap;
vector<int>barr(arr.size());
int cur = ori;
while (1)
{
barr[cur] = 1;
for (auto next : arr[cur])
{
if (dij[next] > dij[cur] + d[next])
{
dij[next] = dij[cur] + d[next];
heap.push(dis_node(dij[next], next));
}
d[next]--;
}
while (heap.size())
{
if (barr[heap.top().next] == 0)
break;
heap.pop();
}
if (heap.size() == 0)break;
cur = heap.top().next;
heap.pop();
}
}
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<int>>arr(n+1);
vector<int>dij(n+1,INT_MAX);
dij[n] = 0;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
arr[b].push_back(a);
d[a]++;
//a的走法,我们是倒着算的,其实正着是a -> b,
//我们是用b算的某个a,而且可以认为是当前最小a可走!!!
}
dijkstr(dij, arr, n, 1);
cout << dij[1] << endl;
}