/*==================================================*\
| DAG的深度优先搜索标记
| INIT: edge[][]邻接矩阵; pre[], post[], tag全置0;
| CALL: dfstag(i, n); pre/post:开始/结束时间
\*==================================================*/
int edge[V][V], pre[V], post[V], tag;
void dfstag(int cur, int n)
{ // vertex: 0 ~ n-1
pre[cur] = ++tag;
for (int i=0; i<n; ++i) if (edge[cur][i]) {
if (0 == pre[i]) {
printf("Tree Edge!\n");
dfstag(i, n);
} else {
if (0 == post[i]) printf("Back Edge!\n");
else if (pre[i] > pre[cur]) printf("Down Edge!\n");
else printf("Cross Edge!\n");
}
}
post[cur] = ++tag;
}
/*==================================================*\
| 无向图找桥
| INIT: edge[][]邻接矩阵;vis[],pre[],anc[],bridge 置0;
| CALL: dfs(0, -1, 1, n);
\*==================================================*/
int bridge, edge[V][V], anc[V], pre[V], vis[V];
void dfs(int cur, int father, int dep, int n)
{ // vertex: 0 ~ n-1
if (bridge) return;
vis[cur] = 1; pre[cur] = anc[cur] = dep;
for (int i=0; i<n; ++i) if (edge[cur][i]) {
if (i != father && 1 == vis[i]) {
if (pre[i] < anc[cur]) anc[cur] = pre[i];//back edge
}
if (0 == vis[i]) { //tree edge
dfs(i, cur, dep+1, n);
if (bridge) return;
if (anc[i] < anc[cur]) anc[cur] = anc[i];
if (anc[i] > pre[cur]) { bridge = 1; return; }
}
}
vis[cur] = 2;
}
/*==================================================*\
| 无向图连通度(割)
| INIT: edge[][]邻接矩阵;vis[],pre[],anc[],deg[]置为0;
| CALL: dfs(0, -1, 1, n);
| k=deg[0], deg[i]+1(i=1…n-1)为删除该节点后得到的连通图个数
| 注意:0作为根比较特殊!
\*==================================================*/
int edge[V][V], anc[V], pre[V], vis[V], deg[V];
void dfs(int cur, int father, int dep, int n)
{// vertex: 0 ~ n-1
int cnt = 0;
vis[cur] = 1; pre[cur] = anc[cur] = dep;
for (int i=0; i<n; ++i) if (edge[cur][i]) {
if (i != father && 1 == vis[i]) {
if (pre[i] < anc[cur])
anc[cur] = pre[i]; //back edge
}
if (0 == vis[i]) { //tree edge
dfs(i, cur, dep+1, n);
++cnt; // 分支个数
if (anc[i] < anc[cur]) anc[cur] = anc[i];
if ((cur==0 && cnt>1) || (cnt!=0 && anc[i]>=pre[cur]))
++deg[cur]; // link degree of a vertex
}
}
vis[cur] = 2;
}
/*==================================================*\
| 最大团问题 DP + DFS
| INIT: g[][]邻接矩阵;
| CALL: res = clique(n);
\*==================================================*/
int g[V][V], dp[V], stk[V][V], mx;
int dfs(int n, int ns, int dep){
if (0 == ns) {
if (dep > mx) mx = dep;
return 1;
}
int i, j, k, p, cnt;
for (i = 0; i < ns; i++) {
k = stk[dep][i]; cnt = 0;
if (dep + n - k <= mx) return 0;
if (dep + dp[k] <= mx) return 0;
for (j = i + 1; j < ns; j++) {
p = stk[dep][j];
if (g[k][p]) stk[dep + 1][cnt++] = p;
}
dfs(n, cnt, dep + 1);
}
return 1;
}
int clique(int n){
int i, j, ns;
for (mx = 0, i = n - 1; i >= 0; i--) {
// vertex: 0 ~ n-1
for (ns = 0, j = i + 1; j < n; j++)
if (g[i][j]) stk[1][ ns++ ] = j;
dfs(n, ns, 1); dp[i] = mx;
}
return mx;
}
/*==================================================*\
| 欧拉路径O(E)
| INIT: adj[][]置为图的邻接表; cnt[a]为a点的邻接点个数;
| CALL: elpath(0); 注意:不要有自向边
\*==================================================*/
int adj[V][V], idx[V][V], cnt[V], stk[V], top;
int path(int v) {
for (int w ; cnt[v] > 0; v = w) {
stk[ top++ ] = v;
w = adj[v][ --cnt[v] ];
adj[w][ idx[w][v] ] = adj[w][ --cnt[w] ];
// 处理的是无向图—-边是双向的,删除v->w后,还要处理删除w->v
}
return v;
}
void elpath (int b, int n){ // begin from b
int i, j;
for (i = 0; i < n; ++i) // vertex: 0 ~ n-1
for (j = 0; j < cnt[i]; ++j)
idx[i][ adj[i][j] ] = j;
printf("%d", b);
for (top = 0; path(b) == b && top != 0; ) {
b = stk[ --top ];
printf("-%d", b);
}
printf("\n");
}
/*==================================================*\
| Dijkstra数组实现 O(N^2)
| Dijkstra --- 数组实现(在此基础上可直接改为STL的Queue实现)
| lowcost[] --- beg到其他点的近距离
| path[] -- beg为根展开的树,记录父结点
\*==================================================*/
#define INF 0x03F3F3F3F
const int N;
int path[N], vis[N];
void Dijkstra(int cost[][N], int lowcost[N], int n, int beg) {
int i, j, min;
memset(vis, 0, sizeof(vis));
vis[beg] = 1;
for (i=0; i<n; i++) {
lowcost[i] = cost[beg][i];
path[i] = beg;
}
lowcost[beg] = 0;
path[beg] = -1; // 树根的标记
int pre = beg;
for (i=1; i<n; i++) {
min = INF;
for (j=0; j<n; j++)
// 下面的加法可能导致溢出,INF不能取太大
if (vis[j]==0 && lowcost[pre]+cost[pre][j]<lowcost[j]) {
lowcost[j] = lowcost[pre] + cost[pre][j];
path[j] = pre;
}
for (j=0; j<n; j++)
if (vis[j] == 0 && lowcost[j] < min){
min = lowcost[j];
pre = j;
}
vis[pre] = 1;
}
}
/*==================================================*\
| Dijkstra O(E * log E)
| INIT: 调用init(nv, ne)读入边并初始化;
| CALL: dijkstra(n, src); dist[i]为src到i的短距离
\*==================================================*/
#define typec int // type of cost const
typec inf = 0x3f3f3f3f; // max of cost
typec cost[E], dist[V];
int e, pnt[E], nxt[E], head[V], prev[V], vis[V];
struct qnode {
int v; typec c;
qnode (int vv = 0, typec cc = 0) : v(vv), c(cc) {}
bool operator < (const qnode& r) const { return c>r.c; }
};
void dijkstra(int n, const int src) {
qnode mv;
int i, j, k, pre;
priority_queue<qnode> que;
vis[src] = 1; dist[src] = 0;
que.push(qnode(src, 0));
for (pre = src, i=1; i<n; i++) {
for (j = head[pre]; j != -1; j = nxt[j]) {
k = pnt[j];
if (vis[k] == 0 && dist[pre] + cost[j] < dist[k]){
dist[k] = dist[pre] + cost[j];
que.push(qnode(pnt[j], dist[k]));
prev[k] = pre;
}
}
while (!que.empty() && vis[que.top().v] == 1) que.pop();
if (que.empty()) break;
mv = que.top(); que.pop();
vis[pre = mv.v] = 1;
}
}
inline void addedge(int u, int v, typec c) {
pnt[e] = v; cost[e] = c;
nxt[e] = head[u]; head[u] = e++;
}
void init(int nv, int ne) {
int i, u, v;
typec c;
e = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(prev, -1, sizeof(prev));
for (i = 0; i < nv; i++) dist[i] = inf;
for (i = 0; i < ne; ++i) {
scanf("%d%d%d", &u, &v, &c); // %d: type of cost
addedge(u, v, c); // vertex: 0 ~ n-1, 单向边
}
}
/*==================================================*\
| BellmanFord单源最短路O(VE)
| 能在一般情况下,包括存在负权边的情况下,解决单源短路径问题
| INIT: edge[E][3]为边表
| CALL: bellman(src);有负环返回0;dist[i]为src到i的短距
| 可以解决差分约束系统: 需要首先构造约束图,构造不等式时>=表示求 小值,
| 作为长路,<=表示求最大值, 作为最短路 (v-u <= c:a[u][v] = c)
\*==================================================*/
#define typec int // type of cost
const typec inf=0x3f3f3f3f; // max of cost
int n, m, pre[V], edge[E][3];
typec dist[V];
int relax (int u, int v, typec c) {
if (dist[v] > dist[u] + c) {
dist[v] = dist[u] + c;
pre[v] = u;
return 1;
}
return 0;
}
int bellman (int src) {
int i, j;
for (i=0; i<n; ++i) {
dist[i] = inf; pre[i] = -1;
}
dist[src] = 0; bool flag;
for (i=1; i<n; ++i) {
flag = false; // 优化
for (j=0; j<m; ++j) {
if( 1 == relax(edge[j][0], edge[j][1], edge[j][2]) )
flag = true;
}
if( !flag ) break;
}
for (j=0; j<m; ++j) {
if (1 == relax(edge[j][0], edge[j][1], edge[j][2]))
return 0; // 有负圈
}
return 1;
}
/*==================================================*\
| 第K短路(Dijkstra)
| dij变形,可以证明每个点经过的次数为小于等于K,所有把dij的数组dist
| 由一维变成2维,记录经过该点1次,2次。。。k次的小值。
| 输出dist[n-1][k]即可
\*==================================================*/
//WHU1603 int g[1010][1010];
int n,m,x;
const int INF=1000000000;
int v[1010]; int dist[1010][20];
int main() {
while (scanf("%d%d%d",&n,&m,&x)!=EOF) {
for (int i=1; i<=n; i++)
for (int j=1;j<=n;j++)
g[i][j]=INF;
for (int i=0; i<m; i++) {
int p,q,r;
scanf("%d%d%d",&p,&q,&r);
if (r<g[p][q]) g[p][q]=r;
}
for (int i=1;i<=n;i++) {
v[i]=0;
for (int j=0;j<=x;j++)
dist[i][j]=INF;
}
dist[1][0]=0;
dist[0][0]=INF;
while (1) {
int k=0;
for (int i=1;i<=n;i++)
if (v[i]<x && dist[i][v[i]]<dist[k][0])
k=i;
if (k==0) break;
if (k==n && v[n]==x-1) break;
for (int i=1;i<=n;i++) {
if (v[i]<x && dist[k][v[k]]+g[k][i]<dist[i][x]) {
dist[i][x]=dist[k][v[k]]+g[k][i];
for (int j=x;j>0;j--)
if (dist[i][j]<dist[i][j-1])
swap(dist[i][j],dist[i][j-1]);
}
}
v[k]++;
}
if (dist[n][x-1]<INF) printf("%d\n",dist[n][x-1]);
else printf("-1\n");
}
return 0;
}
/*==================================================*\
| Prim求MST
| INIT: cost[][]耗费矩阵(inf为无穷大);
| CALL: prim(cost, n); 返回-1代表原图不连通;
\*==================================================*/
#define typec int // type of cost
const typec inf = 0x3f3f3f3f; // max of cost
int vis[V]; typec lowc[V];
typec prim(typec cost[][V], int n) // vertex: 0 ~ n-1
{
int i, j, p;
typec minc, res = 0;
memset(vis, 0, sizeof(vis));
vis[0] = 1;
for (i=1; i<n; i++) lowc[i] = cost[0][i];
for (i=1; i<n; i++) {
minc = inf; p = -1;
for (j=0; j<n; j++)
if (0 == vis[j] && minc > lowc[j]) {
minc = lowc[j]; p = j;
}
if (inf == minc) return -1; // 原图不连通
res += minc; vis[p] = 1;
for (j=0; j<n; j++)
if (0 == vis[j] && lowc[j] > cost[p][j])
lowc[j] = cost[p][j];
}
return res;
}
/*==================================================*\
| 有向图最小树形图
| INIT: eg置为边表; res置为0; cp[i]置为i;
| CALL: dirtree(root, nv, ne); res是结果;
\*==================================================*/
#define typec int // type of res
const typec inf = 0x3f3f3f3f; // max of res
typec res, dis[V];
int to[V], cp[V], tag[V];
struct Edge { int u, v; typec c; } eg[E];
int iroot(int i){
if (cp[i] == i) return i;
return cp[i] = iroot(cp[i]);
}
int dirtree(int root, int nv, int ne) // root: 树根
{ // vertex: 0 ~ n-1
int i, j, k, circle = 0;
memset(tag, -1, sizeof(tag));
memset(to, -1, sizeof(to));
for (i = 0; i < nv; ++i) dis[i] = inf;
for (j = 0; j < ne; ++j) {
i = iroot(eg[j].u);
k = iroot(eg[j].v);
if (k != i && dis[k] > eg[j].c) {
dis[k] = eg[j].c;
to[k] = i;
}
}
to[root] = -1; dis[root] = 0; tag[root] = root;
for (i = 0; i < nv; ++i) if (cp[i] == i && -1 == tag[i]) {
j = i;
for ( ; j != -1 && tag[j] == -1; j = to[j])
tag[j] = i;
if (j == -1) return 0;
if (tag[j] == i) {
circle = 1; tag[j] = -2;
for (k = to[j]; k != j; k = to[k])
tag[k] = -2;
}
}
if (circle) {
for (j = 0; j < ne; ++j) {
i = iroot(eg[j].u);
k = iroot(eg[j].v);
if (k != i && tag[k] == -2)
eg[j].c -= dis[k];
}
for (i = 0; i < nv; ++i) if (tag[i] == -2) {
res += dis[i]; tag[i] = 0;
for (j = to[i]; j != i; j = to[j]) {
res += dis[j]; cp[j] = i; tag[j] = 0;
}
}
if (0 == dirtree(root, nv, ne)) return 0;
} else {
for (i = 0; i < nv; ++i)
if (cp[i] == i) res += dis[i];
}
return 1; // 若返回0代表原图不连通
}
/*==================================================*\
| Tarjan强连通分量
| INIT: vec[]为邻接表; stop, cnt, scnt置0; pre[]置-1;
| CALL: for(i=0; i<n; ++i) if(-1==pre[i]) tarjan(i, n);
\*==================================================*/
vector<int> vec[V];
int id[V], pre[V], low[V], s[V], stop, cnt, scnt;
void tarjan(int v, int n) // vertex: 0 ~ n-1
{
int t, minc = low[v] = pre[v] = cnt++;
vector<int>::iterator pv;
s[stop++] = v;
for (pv = vec[v].begin(); pv != vec[v].end(); ++pv) {
if(-1 == pre[*pv]) tarjan(*pv, n);
if(low[*pv] < minc) minc=low[*pv];
}
if(minc < low[v]) {
low[v] = minc; return;
}
do {
id[t = s[--stop]] = scnt;
low[t] = n;
} while(t != v);
++scnt; // 强连通分量的个数
}
/*==================================================*\
| 拓扑排序
| INIT:edge[][]置为图的邻接矩阵;count[0…i…n-1]:顶点i的入度.
\*==================================================*/
void TopoOrder(int n){
int i, top = -1;
for( i=0; i < n; ++i ) {
if( count[i] == 0 ) { // 下标模拟堆栈
count[i] = top; top = i;
}
for( i=0; i < n; ++i ) {
if( top == -1 ) {
printf("存在回路\n");
return ;
} else {
int j = top; top = count[top];
printf("%d", j);
for( int k=0; k < n; ++k )
if( edge[j][k] && (--count[k]) == 0 ){
count[k] = top; top = k;
}
}
}
}
}
/*==================================================*\
| 有向图最小点基(邻接阵)O(n^2)
| 点基B满足:对于任意一个顶点Vj,一定存在B中的一个Vi,使得Vi是Vj
| 的前代。
\*==================================================*/
//返回点基大小和点基
//传入图的大小n和邻接阵mat,不相邻点边权0
//需要调用强连通分支
#define MAXN 100
int base_vertex(int n,int mat[][MAXN],int* sets) {
int ret=0,id[MAXN],v[MAXN],i,j;
j=find_components(n,mat,id);
for (i=0;i<j;v[i++]=1);
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if (id[i]!=id[j]&&mat[i][j])
v[id[j]-1]=0;
for (i=0;i<n;i++)
if (v[id[i]-1])
v[id[sets[ret++]=i]-1]=0;
return ret;
}