NOIP 2017 Senior 3
来源:互联网 发布:主力如何拉升股价 知乎 编辑:程序博客网 时间:2024/05/21 03:17
传送门(JZOJ)
这个题,很明显要用动态规划求解。但是这个图不是 DAG,还有零环,怎么做呢?我们逐一突破。
1.对于 10% 的数据
首先你得先会最短路吧。由于是稀疏图,使用 SPFA 即可。这样我们就知道了从 1 到 n 的最短距离。猜想,对于 10% 的这么小的数据,爆搜应该没有问题吧?由于可能走环,因此我们只能把已走过的路程作为搜索的出口。理论上能拿 10 分,由于官方数据相当水,这么做能得到 20 分。
2.对于 30% 的数据(k = 0,不存在零边)
当 k = 0 时,答案会是 1 吗?不一定,可能最短路有多条,但是最短路一定不会存在环。这样,我们就可以套用一个经典做法:构造最短路图,即所有边都是最短路上的一条边。方法是:若 f[i]
为走到 i 的路径条数,则边界条件为 f[1] = 1。
注意,由于是从起点到终点方向的递推,因此一定要拓扑排序才不会有后效性!(考试时我居然忘了)
3.对于 70% 的数据(不存在零边)
很自然会想到设 f[i][j]
,表示走到点 j 且当前路径总长度比从 1 到 j 的最短路的长度大 i 的方案数。但是怎么递推呢?边界条件显然是 f[0][1] = 1
。然后考虑最外层循环到底是谁。如果最外层循环是结点,k 的循环放内层的话,是有毛病的。因为可能会从一个点出发,走到一个已经计算过的点。由于最外层循环是结点,那个已经计算过的点显然不会再被访问,就造成了后效性。若最外层循环为 k,里面放结点就没有毛病了。可以知道,若要从 f[a][b] -> f[c][d]
,那么 c 一定是大于 a 的(不存在零边!),从而说明 f[c][d]
是没有作为起点更新过其它答案的,这样就不会有后效性了。
以上递推要遍历每一条边。
最终答案为
4.对于 100% 的数据
如果有零边,我们考虑如何找到零环。首先我们仍然构造出一个最短路径图。注意如果有满足要求的零环存在(要输出 -1 时),则零环上的任意一点都应该满足 dis[i] + disT[i] <= dis[n] + k
(disT 表示从 n 到任意一点的最短路)。这样,若拓扑排序后还有点没有排到,且有任意一点满足以上条件,就输出 -1,否则对已经排到的点进行 3 中的DP。可以知道,这个 DP 一定不会从零环上的点出发(因为没有把它们加入拓扑序),所以即使存在一个零环但是正确答案不是 -1 时也能得到正确答案。(然而官方数据并不卡这一点,如果你直接判断是否有点没有被拓扑排序到就输出 -1 也是可以过官方数据的,但事实上这是错的)
参考数据
17 8 1 1000001 2 12 3 13 4 04 3 01 5 11 6 15 7 06 7 0
2
而不是 -1。
参考代码(渣,真的仅供参考)
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef int INT;INT readIn(){ INT a = 0; bool minus = false; char ch = getchar(); while (!(ch == '-' || (ch >= '0' && ch <= '9'))) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a = a * 10 + (ch - '0'); ch = getchar(); } if (minus) a = -a; return a;}void printOut(INT x){ char buffer[20]; INT length = 0; bool minus = x < 0; if (minus) x = -x; do { buffer[length++] = x % 10 + '0'; x /= 10; } while (x); if (minus) buffer[length++] = '-'; do { putchar(buffer[--length]); } while (length); putchar('\n');}const INT INF = (~(INT(1) << (sizeof(INT) * 8 - 1)));const INT maxk = 55;const INT maxn = 100005;const INT maxm = 200005;INT n, m, K, mod;struct Edge{ INT to; INT cost; INT next;} edges[maxm], edgesT[maxm];INT head[maxn], headT[maxm]; //G and GTINT count_;void addEdge(INT from, INT to, INT cost){ count_++; edges[count_].to = to; edges[count_].cost = cost; edges[count_].next = head[from]; head[from] = count_; edgesT[count_].to = from; edgesT[count_].cost = cost; edgesT[count_].next = headT[to]; headT[to] = count_;}INT d; //no useINT minC; //for cheat//a mess, so comment is needed//shortest path, dis means 1 to others, disT means n to othersnamespace SP{ INT dis[maxn]; INT disT[maxn]; bool vis[maxn]; struct Queue { INT c[maxn]; INT head, tail; Queue() : head(), tail() {} void clear() { head = tail = 0; } bool empty() { return head == tail; } void push(INT x) { c[tail] = x; tail = (tail + 1) % maxn; } INT front() { return c[head]; } void pop() { head = (head + 1) % maxn; } } q; INT s; //bad code void SPFA() { memset(vis, 0, sizeof(vis)); q.clear(); q.push(1); dis[1] = 0; vis[1] = true; while (!q.empty()) { INT from = q.front(); q.pop(); vis[from] = false; for (int i = head[from]; i; i = edges[i].next) { Edge& e = edges[i]; INT to = e.to; INT c = e.cost; if (dis[from] + c < dis[to]) { dis[to] = dis[from] + c; if (!vis[to]) { q.push(to); vis[to] = true; } } } } memset(vis, 0, sizeof(vis)); q.clear(); q.push(n); disT[n] = 0; vis[n] = true; while (!q.empty()) { INT from = q.front(); q.pop(); vis[from] = false; for (int i = headT[from]; i; i = edgesT[i].next) { Edge& e = edgesT[i]; INT to = e.to; INT c = e.cost; if (disT[from] + c < disT[to]) { disT[to] = disT[from] + c; if (!vis[to]) { q.push(to); vis[to] = true; } } } } } INT goSP() { memset(dis, 0x3f, sizeof(dis)); memset(disT, 0x3f, sizeof(disT)); SPFA(); return dis[n]; }};#define RunInstance(x) delete new xstruct work{ INT topo[maxn]; INT inDegree[maxn]; bool vis[maxn]; INT found; //if a vertex on a "0 ring" is valid (dis[i] + disT[i] <= dis[1] + K), print -1 //use topo sort to find vertexes on "0 ring" void BFS() //topo sort { using namespace SP; for (int i = 1; i <= n; i++) { for (int j = head[i]; j; j = edges[j].next) { Edge& e = edges[j]; INT to = e.to; INT cost = e.cost; if (dis[i] + cost == dis[to]) inDegree[to]++; } } q.clear(); for (int i = n; i >= 1; i--) if (!inDegree[i]) q.push(i); while (!q.empty()) { INT from = q.front(); vis[from] = true; topo[++topo[0]] = from; found++; q.pop(); for (int j = head[from]; j; j = edges[j].next) { Edge& e = edges[j]; INT to = e.to; INT cost = e.cost; if (dis[from] + cost == dis[to]) { inDegree[to]--; if (!inDegree[to]) q.push(to); } } } if (found != n) for (int i = 1; i <= n; i++) if (!vis[i] && dis[i] + disT[i] <= dis[n] + K) { found = -1; return; } } INT f[maxk][maxn]; work() : f(), vis(), found(), inDegree() { using namespace SP; topo[0] = 0; BFS(); if (found == -1) { printOut(-1); return; } //otherwise, this is a DAG f[0][1] = 1; for (int k = 0; k <= K; k++) { for (int i = 1; i <= topo[0]; i++) { INT node = topo[i]; for (int j = head[node]; j; j = edges[j].next) { Edge& e = edges[j]; INT to = e.to; INT c = e.cost; INT s2 = k + dis[node] + c - dis[to]; if (s2 <= K) { f[s2][to] = (f[s2][to] + f[k][node]) % mod; } } } } INT ans = 0; for (int i = 0; i <= K; i++) ans = (ans + f[i][n]) % mod; printOut(ans); }};void run(){ INT T = readIn(); while (T--) { count_ = 0; memset(head, 0, sizeof(head)); memset(headT, 0, sizeof(headT)); n = readIn(); m = readIn(); K = readIn(); mod = readIn(); minC = INF; for (int i = 1; i <= m; i++) { INT u = readIn(); INT v = readIn(); INT c = readIn(); minC = std::min(minC, c); addEdge(u, v, c); } d = SP::goSP(); RunInstance(work); }}int main(){#ifndef LOCAL freopen("park.in", "r", stdin); freopen("park.out", "w", stdout);#endif run(); return 0;}
- NOIP 2017 Senior 3
- NOIP 2017 Senior 1
- NOIP 2017 Senior 5
- NOIP 2017 Senior 6
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2012 Senior 3
- NOIP 2015 Senior 3
- NOIP 2014 Senior 3
- NOIP 2013 Senior 3
- NOIP 2016 Senior 3
- NOIP 2003 Senior 3
- NOIP 2005 Senior 3
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2011 Senior 2
- NOIP 2011 Senior 4
- NOIP 2011 Senior 5
- PHP基于event的webSocket连接实例
- GLSurfaceView渲染过程详解
- Android ADB 一些常用命令
- c#委托和事件(一)
- 为什么不要使用 ::before{content:url('xxx')} 设置图标
- NOIP 2017 Senior 3
- JavaWeb项目运行报错:Failed to instantiate [com.sun.tools.xjc.model.Model]
- Maven VS Gradle
- C++的const类成员函数
- 利用引用交换俩指针的指向关系
- Http报头Accept与Content-Type的区别
- ListView嵌套卡顿问题分析及解决
- C++Primer第五版中的c++11特性归纳(二)
- 动态规划