堆的核心代码:
void sink(int p)//下沉操作
{
int q = p << 1, a = heap[p];
while(q <= hs)
{
if(q < hs && heap[q+1] < heap[q]) q++;
if(heap[q] >= a) break;
heap[p] = heap[q];
p = q;
q = p <<1;
}
heap[p] = a;
}
int deleteMin()//删除操作
{
int r = heap[1];
heap[1] = heap[hs--];
sink(1);
return r;
}
void swim(int p)//上浮操作
{
int q = p >> 1, a = heap[p];
while(q && a < heap[q])
{
heap[p] = heap[q];
p = q;
q = p>>1;
}
heap[p] = a;
}
void insert(int a)//插入数据
{
heap[++hs] = a;
swim(hs);
}
/***************************
并差集
****************************/
void makeset(int x)
{
rank[x] = 0;
p[x] = x;
}
int findset(int x)
{
if(p[x] != x)
p[x] = findset(p[x]);
return p[x];
}
void unionset(int x, int y)
{
x = findset(x);
y = findset(y);
if(rank[x] > rank[y]) p[y] = x;
else
{
p[x] = y;
if(rank[x] == rank[y])
rank[y]++;
}
}
10进制数像任意数制转换
while (scanf("%ld%d",&x,&base)!=EOF)
{
i=0;
while (x!=0)
{
a[i]=x % base;
if (a[i]<0)
{
if (base>0) a[i]=base+a[i];
else a[i]=(-base)+a[i];
}
x=(x-a[i])/base;
i++;
}
for (j=i-1;j>=0;j--)
{
if (a[j]<10) printf("%d",a[j]);
else printf("%c", a[j]+55);
}
printf("/n");
}
//约瑟夫扔人
int n, m;
while(scanf("%d%d", &n, &m), m||n)
{
int i;
int ans = 0;
for(i = 2; i <= n; i++ )
ans = (ans + m)% i;
printf("%d %d %d/n", n, m, ans + 1);
}
图论中的floyd:
for(k = 0; k < n; k++ )
for(i = 0; i < n; i++ )
for(j = 0; j < n; j++ )
if(min[i][k] + min[k][j] < min[i][j])
min[i][j] = min[i][k] + min[k][j];
筛素数:见joj模版
图论中的djistra:见joj模版
图论中的最小生成树:kruskal如下:
/*
poj 2485题
求一个图的最小生成树的一个最大边
用克鲁斯卡尔算法求出生成树
用并差集实现
另外大数输入最好用scanf
本算法时间耗费405ms
有点费时
Highways
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 3702 Accepted: 1655
Description
The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has no public highways. So the traffic is difficult in Flatopia. The Flatopian government is aware of this problem. They're planning to build some highways so that it will be possible to drive between any pair of towns without leaving the highway system.
Flatopian towns are numbered from 1 to N. Each highway connects exactly two towns. All highways follow straight lines. All highways can be used in both directions. Highways can freely cross each other, but a driver can only switch between highways at a town that is located at the end of both highways.
The Flatopian government wants to minimize the length of the longest highway to be built. However, they want to guarantee that every town is highway-reachable from every other town.
Input
The first line of input is an integer T, which tells how many test cases followed.
The first line of each case is an integer N (3 <= N <= 500), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 65536]) between village i and village j. There is an empty line after each test case.
Output
For each test case, you should output a line contains an integer, which is the length of the longest road to be built such that all the villages are connected, and this value is minimum.
Sample Input
1
3
0 990 692
990 0 179
692 179 0
Sample Output
692
Hint
Huge input,scanf is recommended.
Source
*/
#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;
struct edge
{
int x;
int y;
double num;
}
a[99999];
bool operator <( const edge &a1, const edge &a2)
{
return a1.num < a2.num ;
}
int rank[99999] , parent[99999];
double sum;
int i, j, k, t, m , n , N ,l;
void makeset( int x )
{
rank[x] = 0;
parent[x] = x;
}
int findset( int x )
{
if( parent[x] == x )
return x;
int px = x , temp ;
while( px != parent[px]) px = parent[px];
while( parent[x] != px )
{
temp = parent[x];
parent[x] = px;
x = temp;
}
return px;
}
void unionset( int x, int y ,int temp)
{
x = findset( x );
y = findset( y );
sum = a[temp].num;
if( rank[x] > rank[y] )
{
parent[y] = x;
}
else
{
parent[x] = y;
if(rank[x] == rank[y])
rank[y]++;
}
}
int main()
{
scanf("%d",&N) ;
for( l=1 ; l <= N; l++ )
{
scanf("%d",&n);
t = 1;
sum = 0;
for( i = 1; i <= n ; i++ )
for( j = 1; j <= n ; j++ )
{
scanf("%lf",&a[t++].num);
a[t-1].x = i;
a[t-1].y = j;
}
for( i = 1; i <= n ; i++ )
makeset( i );
sort(a+1,a+t);
for( i = 1; i < t ; i++ )
if( findset(a[i].x) != findset(a[i].y) )
unionset( a[i].x , a[i].y , i ) ;
cout.precision(15);
cout<<sum<<endl;
}
return 0 ;
}
prim见算法书:现写
逆序数+模式匹配见zoj模版:
/*********************************************
LCA问题:
离线的Tarjon算法:
承接上部并差集:
*********************************************/
#include <iostream>
using namespace std;
int tree[901][901];
int d[901];
int rank[901];
int parent[901];
bool used[901];
int anc[901];
int qurry[901][901];
int res[901];
int n, i, j, k, t, nod, m;
int temp1, temp2;
void make_set(int num)
{
parent[num] = num;
rank[num] = 0 ;
}
int find_set(int num)
{
if(num != parent[num])
parent[num] = find_set(parent[num]);//path compression
return parent[num];
}
void union_set(int u, int v)
{
u = find_set(u);
v = find_set(v);
if(rank[u]> rank[v])
{
parent[v] = u;
}
else
{
parent[u] = v;
if(rank[v ] == rank[u])
rank[v] ++;
}
}
void Tarjan(int num)
{
int i;
make_set(num);
anc[num] = num;
for(i = 1; i <= tree[num][0]; i++ )
{
Tarjan(tree[num][i]);
union_set(num, tree[num][i]);
anc[find_set(num)] = num;
}
used[num] = 1;
for(i = 1; i <= qurry[num][0]; i++ )
{
if(used[qurry[num][i]])
res[anc[find_set(qurry[num][i])]]++;
}
}
int main()
{
while(scanf("%d", &n) != -1)
{
char c1, c2, c3;
memset(rank, 0 , sizeof(rank));
memset(qurry, 0, sizeof(qurry));
memset(d, 0, sizeof(d));
memset(used, 0, sizeof(used));
memset(res, 0, sizeof(res));
for( i = 1; i <= n; i++)
{
scanf("%d%c%c%d%c" ,&nod, &c1, &c2, &k, &c3 );
tree[nod][0] = k;
for(j = 1; j <= k ; j++ )
{
scanf("%d", &tree[nod][j]);
d[tree[nod][j]]++;
}
}
scanf("%d", &m);
for(i = 1; i <= m; i++ )
{
while((c1 = getchar()) != '(' );
scanf("%d%d)", &temp1, &temp2);
qurry[temp1][++qurry[temp1][0]] = temp2;
qurry[temp2][++qurry[temp2][0]] = temp1;
}
for(i = 1; i <= n; i++ )
if(!d[i])
break;
Tarjan(i);
for(i = 1; i <= n; i++ )
if(res[i])
printf("%d:%d/n", i, res[i]);
}
return 0;
}
/****************************************************************
在线的Lca转Rmq:
深搜—o(1)
****************************************************************/
/*
例二
对数列:5,8,1,3,6,4,9,5,7 有:
RMQ(2,4)=3
RMQ(6,9)=6
RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。下面给出LCA问题向RMQ问题的转化方法。
对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的深度存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求E[RMQ(D,R[u],R [v])],(R[u]<R[v])。例如,对于第一节的例一,求解步骤如下:
数列E[i]为:1,2,1,3,4,3,5,3,1
R[i]为:1,2,4,5,7
D[i]为:0,1,0,1,2,1,2,1,0
于是有:
LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1
LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3
LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3
易知,转化后得到的数列长度为树的结点数的两倍加一,所以转化后的RMQ问题与LCA问题的规模同次
文章出处:http://www.diybl.com/course/3_program/c++/cppsl/2008920/143985.html
总结起来就是dfs+ o(n)的算法
*/
#include <iostream>
using namespace std;
#define N 1001
int tree[N][N];
int i, j, k, t;
int E[100*N];
int D[100*N];
int R[N];
int used[N];
int n, m;
int flag;
void dfs(int root, int step)
{
E[flag++] = root;
D[flag-1] = step;
if(!used[root])
{
used[root] = 1;
R[root] = flag - 1;
}
int i ;
for(i = 1; i <= tree[root][0]; i++ )
{
dfs(tree[root][i], step+1);
E[flag++] = root;
D[flag-1] = step;
}
}
void solve(int a, int b)
{
int i ;
int min = INT_MAX;
int pos = -1;
if( R[b] < R[a])
swap(a, b);
for(i = R[a]; i <= R[b]; i++ )
{
if(D[i] < min)
{
min = D[i];
pos = i;
}
}
cout<<E[pos]<<endl;
}
int main()
{
while(scanf("%d", &n), n)
{
int t1, t2;
memset(tree, 0 , sizeof(tree));
memset(used, 0, sizeof(used));
for(i = 1; i <= n; i++ )
{
scanf("%d%d", &t1, &t2);
tree[t2][++tree[t2][0]] = t1;
}
flag= 0;
dfs(0, 0);
scanf("%d", &m);
for(i = 1; i <= m; i++ )
{
scanf("%d%d", &t1, &t2);
solve(t1, t2);
}
}
return 0;
}
网络流,二分图的匹配,bellman—ford问题:见joj与zoj的模版:
组合数
int C (int n , int m )
{
if(m == 0)
return 1;
else
return n*C( n - 1, m - 1)/m;
}
离散化, 与线段树:
坐标离散化:
for(i = 1; i <= n; i++ )
scanf("%d%d%d", &line[i].l, &line[i].h, &line[i].r);
for(i = 1; i <= n; i++ )
p_back[2*i-1] = line[i].l, p_back[2*i] = line[i].r;
sort(p_back + 1, p_back + 2*n + 1);
int tmp = -999999;
int tag = 1;
for(i = 1; i <=2*n; i++ )
{
if(p_back[i] != tmp)
{
tmp = p_back[i];
p_back[tag++] = tmp;
}
}
tag --;
线段树的写法:
1, 求矩形的周长:
#include <iostream>
#include <algorithm>
using namespace std;
#define N 20000
#define add 10000
int n;
int xmax[N], xmin[N], ymax[N], ymin[N];
int tmp[N];
int p_ch[10*N];
int p_or[N];
int sum ;
struct treee
{
int l, r, c;
}tree[5*N];
struct listt
{
int x, x1, x2, m;
}line[N];
bool operator <(const listt &a, const listt &b)
{
return a.x < b.x;
}
int i, j, k, t;
void build(int root, int l, int r)
{
tree[root].l = l;
tree[root].r = r;
tree[root].c = 0;
if(l +1 == r)
return ;
int m = (l + r)/2;
build(2*root, l, m);
build(2*root+1, m, r);
}
void ins(int root , int l, int r)
{
if(l >= tree[root].r||r <=tree[root].l)
return;
if(tree[root].l >= l && tree[root].r <= r && tree[root].r - tree[root].l == 1)
{
if(tree[root].c == 0)
sum += p_or[tree[root].r] - p_or[tree[root].l];
tree[root].c++;
return;
}
int m = (l + r)/2;
ins(2*root, l , r);
ins(2*root + 1, l , r);
}
void del(int root, int l, int r)
{
if(l >= tree[root].r||r <= tree[root].l)
return ;
int m = (tree[root].l + tree[root].r)/2;
if(tree[root].l >= l && tree[root].r <= r && tree[root].r - tree[root].l == 1)
{
if(tree[root].c == 1)
sum += p_or[tree[root].r] - p_or[tree[root].l];
tree[root].c --;
return ;
}
del(2*root , l, r);
del(2*root + 1, l, r);
}
int main()
{
while(scanf("%d", &n) != -1)
{
for(i = 0; i < n; i++ )
scanf("%d%d%d%d", xmin+i, ymin+i, xmax+i, ymax+i);
for(i = 0; i < n; i++ )
{
tmp[2*i] = xmin[i];
tmp[2*i+1] = xmax[i];
}
sort(tmp, tmp + 2*i);
int x = 0;
int tmpp = -99999999;
for(i = 0; i < 2*n; i++ )
{
if(tmpp != tmp[i])
{
tmpp = tmp[i];
x++ ;
p_ch[tmp[i] + add ] = x;
p_or[x] = tmp[i];
}
}
for(i = 0 ; i < n; i++ )
{
line[2*i].m = 0;
line[2*i].x = ymin[i];
line[2*i].x1 = p_ch[xmin[i] + add];
line[2*i].x2 = p_ch[xmax[i] + add];
line[2*i + 1].m = 1;
line[2*i + 1].x = ymax[i];
line[2*i + 1].x1 = p_ch[xmin[i] + add];
line[2*i + 1].x2 = p_ch[xmax[i] + add];
}
sort(line, line + 2*n);
build(1, 1, x);
sum = 0;
for(i = 0; i < 2*n; i++ )
{
if(line[i].m == 0)
ins(1, line[i].x1, line[i].x2);
else
del(1, line[i].x1, line[i].x2);
}
for(i = 0; i < n; i++ )
{
tmp[2*i] = ymin[i];
tmp[2*i+1] = ymax[i];
}
sort(tmp, tmp + 2*n);
tmpp = -99999999;
x = 0;
for(i = 0; i < 2*n; i++ )
{
if(tmpp != tmp[i])
{
x++;
p_ch[tmp[i] + add] = x;
p_or[x] = tmp[i];
tmpp = tmp[i];
}
}
for(i = 0; i < n; i++ )
{
line[2*i].m = 0;
line[2*i].x = xmin[i];
line[2*i].x1 = p_ch[ymin[i] + add];
line[2*i].x2 = p_ch[ymax[i] + add];
line[2*i + 1].m = 1;
line[2*i + 1].x = xmax[i];
line[2*i + 1].x1 = p_ch[ymin[i] + add];
line[2*i + 1].x2 = p_ch[ymax[i] + add];
}
sort(line, line + 2*n );
build(1, 1, x);
for(i = 0; i < 2*n; i++ )
{
if(line[i].m == 0)
ins(1, line[i].x1, line[i].x2);
else
del(1, line[i].x1, line[i].x2);
}
printf("%d/n", sum);
}
return 0;
}
(一)巴什博弈(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
很容易想到当n%(m+1)<>0时,先取必胜,第一次先拿走n%(m+1),以后每个回合到保持两人拿走的物品总和为m+1即可。
这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。
(二)威佐夫博弈(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10).可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk=ak+k.
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...,n 方括号表示取整函数)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1。618...,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。
题目如下:
#include <iostream>
using namespace std;
double h = 0.618033988749895;
int n, m;
int main()
{
while(scanf("%d%d", &n, &m) != -1)
{
int x, y;
if(n == m)
{
if(n == 0)
printf("0/n");
else
printf("1/n");
}
else
{
if(n < m)
swap(n, m);
x = n - m;
y = int (double(x)/h);
if(y == m)
printf("0/n");
else
printf("1/n");
}
}
return 0;
}
(三)尼姆博弈(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
对于任何奇异局势(a,b,c),都有a^b^c=0.
非奇异局势(a,b,c)(a<b<c)转换为奇异局势,只需将c变为a^b,即从c中减去 c-(a^b)即可。
寻找必败态——一类博弈问题的快速解法
博弈是信息学和数学试题中常会出现的一种类型,算法灵活多变是其最大特点,而其中有一类试题更是完全无法用常见的博弈树来进行解答。寻找必败态即为针对此类试题给出一种解题思路。
哈希问题只要查模版或者用stl即可
map的应用:
一般与string搭配使用
几个有用的函数如下:
map<string, int> mapp;//建立mapp
mapp.clear();//清空map
mapp.insert(make_pair(Astring, Aintnum));//插入一个元素
一般都如是用:
void ins(string a)
{
mapp.insert(make_pair(a, pos++));
}
map<string, int>::iterator iterr = mapp.find(devs[i]);
if(iterr == mapp.end())
{
ins(devs[i]);
num++;
}
抑或:
map<string, int>::iterator iterr = mapp.find(Astring);
if(iterr != mapp.end() )
{
mapp.erase(iterr);
num--;
}
mapp.size()//map的元素个数
//交换元素
string first( "This comes first" );
string second( "And this is second" );
first.swap( second );
cout << first << endl;
cout << second << endl;
//是否为空
if(mapp.empty()) ……
//set的用法:
见下题:
#include <iostream>
#include <string>
#include <set>
using namespace std;
struct person
{
int p,v;
};
bool operator < (const person & a,const person& b)
{
return a.p < b.p;
}
set<person> bs;
person tp;
int n;
int main()
{
while(scanf("%d", &n) , n)
{
if(n == 1)
{
int p_tmp;
int v_tmp;
scanf("%d%d", &v_tmp, &p_tmp);
person a;
a.p = p_tmp;
a.v = v_tmp;
bs.insert(a);
continue;
}
else if( n == 3)
{
if(bs.empty())
{
printf("0/n");
continue;
}
printf("%d/n", (*bs.begin()).v);
bs.erase(bs.begin());
}
else if(n == 2)
{
if(bs.empty())
{
printf("0/n");
continue;
}
printf("%d/n", (*bs.rbegin()).v);
tp = *bs.rbegin();
bs.erase(bs.find(tp));
}
else
break;
}
return 0;
}
vector的用法:
vector<int> vs//建立一个int型的向量
vs.empty()//同上
vs.begin()//第一个元素的指针
vs.end()//最后一个元素的指针,是空?
vs.size()//vs的大小
vs.clear()//同上
vs[i]//当数组用也是可以的
string的用法
string a;
a.size()//串的大小
a[i]//数组用
//强连通分量
一、 Kosaraju算法
1. 算法思路
基本思路:
这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时应用了原图G和反图GT。(步骤1)先用对原图G进行深搜形成森林(树),(步骤2)然后任选一棵树对其进行深搜(注意这次深搜节点A能往子节点B走的要求是EAB存在于反图GT),能遍历到的顶点就是一个强连通分量。余下部分和原来的森林一起组成一个新的森林,继续步骤2直到 没有顶点为止。
改进思路:
当然,基本思路实现起来是比较麻烦的(因为步骤2每次对一棵树进行深搜时,可能深搜到其他树上去,这是不允许的,强连通分量只能存在单棵树中(由开篇第一句话可知)),我们当然不这么做,我们可以巧妙的选择第二深搜选择的树的顺序,使其不可能深搜到其他树上去。想象一下,如果步骤2是从森林里选择树,那么哪个树是不连通(对于GT来说)到其他树上的呢?就是最后遍历出来的树,它的根节点在步骤1的遍历中离开时间最晚,而且可知它也是该树中离开时间最晚的那个节点。这给我们提供了很好的选择,在第一次深搜遍历时,记录时间i离开的顶点j,即numb[i]=j。那么,我们每次只需找到没有找过的顶点中具有最晚离开时间的顶点直接深搜(对于GT来说)就可以了。每次深搜都得到一个强连通分量。
隐藏性质:
分析到这里,我们已经知道怎么求强连通分量了。但是,大家有没有注意到我们在第二次深搜选择树的顺序有一个特点呢?如果在看上述思路的时候,你的脑子在思考,相信你已经知道了!!!它就是:如果我们把求出来的每个强连通分量收缩成一个点,并且用求出每个强连通分量的顺序来标记收缩后的节点,那么这个顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。为什么呢?首先,应该明确搜索后的图一定是有向无环图呢?废话,如果还有环,那么环上的顶点对应的所有原来图上的顶点构成一个强连通分量,而不是构成环上那么多点对应的独自的强连通分量了。然后就是为什么是拓扑序列,我们在改进分析的时候,不是先选的树不会连通到其他树上(对于反图GT来说),也就是后选的树没有连通到先选的树,也即先出现的强连通分量收缩的点只能指向后出现的强连通分量收缩的点。那么拓扑序列不是理所当然的吗?这就是Kosaraju算法的一个隐藏性质。
2. 伪代码
Kosaraju_Algorithm:
step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束。
3. 实现代码:
#include <iostream>
using namespace std;
const int MAXN = 110;
typedef int AdjTable[MAXN]; //邻接表类型
int n;
bool flag[MAXN]; //访问标志数组
int belg[MAXN]; //存储强连通分量,其中belg[i]表示顶点i属于第belg[i]个强连通分量
int numb[MAXN]; //结束时间标记,其中numb[i]表示离开时间为i的顶点
AdjTable adj[MAXN], radj[MAXN]; //邻接表,逆邻接表
//用于第一次深搜,求得numb[1..n]的值
void VisitOne(int cur, int &sig)
{
flag[cur] = true;
for ( int i=1; i<=adj[cur][0]; ++i )
{
if ( false==flag[adj[cur][i]] )
{
VisitOne(adj[cur][i],sig);
}
}
numb[++sig] = cur;
}
//用于第二次深搜,求得belg[1..n]的值
void VisitTwo(int cur, int sig)
{
flag[cur] = true;
belg[cur] = sig;
for ( int i=1; i<=radj[cur][0]; ++i )
{
if ( false==flag[radj[cur][i]] )
{
VisitTwo(radj[cur][i],sig);
}
}
}
//Kosaraju算法,返回为强连通分量个数
int Kosaraju_StronglyConnectedComponent()
{
int i, sig;
//第一次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=1; i<=n; ++i )
{
if ( false==flag[i] )
{
VisitOne(i,sig);
}
}
//第二次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=n; i>0; --i )
{
if ( false==flag[numb[i]] )
{
VisitTwo(numb[i],++sig);
}
}
return sig;
}
//spfa + 强连通分量很强大,都用邻接表类型
//spfa判断负环时只要记录当任意一点的relax的次数》n即可
#include <iostream>
#include <cmath>
using namespace std;
double inf = 999999999999999999999.0;
const int MAXN = 1010;
typedef int AdjTable[MAXN]; //邻接表类型
int n, m;
bool flag[MAXN];//访问标志数组
int belg[MAXN]; //存储强连通分量,其中belg[i]表示顶点i属于第belg[i]个强连通分量
int numb[MAXN]; //结束时间标记,其中numb[i]表示离开时间为i的顶点
AdjTable adj[MAXN], radj[MAXN]; //邻接表,逆邻接表
int used[MAXN];//该强连通分量是否用过
double cost[MAXN][MAXN];//成本矩阵
int cap[MAXN];//每点在spfa中迭代次数
double dist[MAXN];//各点到原点的最短路
int queue[10000*MAXN];//队列
int i, j, k, t, src;
int t1, t2;
double t3;
//用于第一次深搜,求得numb[1..n]的值
void VisitOne(int cur, int &sig)
{
flag[cur] = true;
for ( int i=1; i<=adj[cur][0]; ++i )
{
if ( false==flag[adj[cur][i]] )
{
VisitOne(adj[cur][i],sig);
}
}
numb[++sig] = cur;
}
//用于第二次深搜,求得belg[1..n]的值
void VisitTwo(int cur, int sig)
{
flag[cur] = true;
belg[cur] = sig;
for ( int i=1; i<=radj[cur][0]; ++i )
{
if ( false==flag[radj[cur][i]] )
{
VisitTwo(radj[cur][i],sig);
}
}
}
//Kosaraju算法,返回为强连通分量个数
int Kosaraju_StronglyConnectedComponent()
{
int i, sig;
//第一次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=1; i<=n; ++i )
{
if ( false==flag[i] )
{
VisitOne(i,sig);
}
}
//第二次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=n; i>0; --i )
{
if ( false==flag[numb[i]] )
{
VisitTwo(numb[i],++sig);
}
}
//for(i = 1; i <= n;i++ )
// printf("!!!%d/n", belg[i]);
return sig;
}
int spfa(int src)
{
int i, j, k;
int u, v;
int p = 0, q = 1;
memset(cap, 0, sizeof(cap));
for(i = 0; i <= n; i++ )
dist[i] = inf;
dist[src] = 0;
queue[0] = src;
cap[src]++;
while(p < q)
{
v = queue[p];
for(i = 1; i <= adj[v][0]; i++ )
{
if(dist[adj[v][i]] > dist[v] + cost[v][adj[v][i]] && belg[v] == belg[adj[v][i]])
{
dist[adj[v][i]] = dist[v] + cost[v][adj[v][i]];
cap[adj[v][i]]++;
queue[q++] = adj[v][i];
if(cap[adj[v][i]] > n)
return 0;
}
}
p++;
}
return 1;
}
void solve()
{
int i, j;
memset(used, 0, sizeof(used));
for(i = 1; i <= n ; i++ )
if(!used[belg[i]])
{
used[belg[i]] = 1;
if(!spfa(i))
{
puts("-1");
return;
}
}
memset(belg, 0, sizeof(belg));
spfa(src);
for(i = 1; i <= n; i++ )
{
if(dist[i] == inf)
puts("NoPath");
else
printf("%.0lf/n", dist[i]);
}
}
int main()
{
while(scanf("%d%d%d", &n , &m , &src) != -1)
{
for(i = 0; i <= n; i++ )
for(j = i; j <= n; j++ )
cost[i][j] = cost[j][i] = inf;
for(i = 0; i <= n; i++ )
adj[i][0] = radj[i][0] = cost[i][i]= 0;
for(i = 0; i < m; i++ )
{
scanf("%d%d%lf", &t1, &t2, &t3);
adj[t1][++adj[t1][0]] = t2;
radj[t2][++radj[t2][0]] = t1;
cost[t1][t2] = cost[t1][t2] < t3 ? cost[t1][t2]: t3;
}
t = Kosaraju_StronglyConnectedComponent();
solve();
}
return 0;
}
/*****************************************
给定纬度精度求球面距a1 a2为纬度 b1 b2 为经度
******************************************/
double val(double a1, double b1, double a2, double b2 )
{
return R*acos(cos(b1/360 *2*pi) * cos(b2/360 *2*pi ) * cos((a1-a2)/360 *2*pi) + sin(b1/360 *2*pi) * sin(b2/360 *2*pi));
}
/***********************************
二维树状数组,回答一个阵中了若干子阵的
和的问题
************************************/
int index(int x)
{
return x&(-x);
}
int add(int x,int y,int k, int m, int n)//a[x][y]+k
{
int i,j;
for(i=x;i<=m;i=i+index(i))
for(j=y;j<=n;j=j+index(j))
c[i][j]+=k;
return 0;
}
int sum(int x,int y)//求以(1,1),(x,y)分别为左上顶点,右下顶点的矩形区域内的和
{
int i,j,s=0;
for(i=x;i>0;i=i-index(i))
for(j=y;j>0;j=j-index(j))
s+=c[i][j];
return s;
}
int search(int x0,int y0,int x1,int y1)//计算以(x0,y0),(x1,y1)分别为左上顶点,右下顶点的矩形区域内的和
{
return sum(x1,y1)-sum(x0-1,y1)-sum(x1,y0-1)+sum(x0-1,y0-1);
}
/***********************************************
kmp算法,回答给定B串,询问其是否为A的子串,并且回答
匹配次数的问题,输入A,B串:scanf("%s", B+1);scanf("%s", A+1);
A串长:n = strlen(A + 1);
B串长:m = strlen(B + 1);
先调用预处理pr_op(B, m, P);
************************************************/
int kmp(char *A, char *B, int n , int m, int *P)
{
int i, j = 0;
int sum = 0;
for(i = 1; i <= n; i++ )
{
while (j > 0&& B[j+1]!=A[i] )
j = P[j];
if(B[j+1]==A[i])
j = j+1;
if(j == m)
{
sum++;
j = P[j];
}
}
return sum;
}
void pr_op(char *B, int m, int *P)
{
P[1] = 0;
int j = 0;
int i;
for( i = 2 ; i <= m ; i++ )
{
while (j > 0&& B[j+1] != B[i])
j = P[j];
if(B[j+1]==B[i])
j = j+1;
P[i] = j;
}
}