ACM Standard Code Library
Huang Wei
Computer Science and Engineering
Association of Programing
Information Engineering College
Hangzhou Dianzi University
April, 2007
ACM 算法模板集
Contents
一. 常用函数与STL
二. 重要公式与定理
1. Fibonacci Number
2. Lucas Number
3. Catalan Number
4. Stirling Number(Second Kind)
5. Bell Number
6. Stirling's Approximation
7. Sum of Reciprocal Approximation
8. Young Tableau
9. 整数划分
10. 错排公式
11. 三角形内切圆半径公式
12. 三角形外接圆半径公式
13. 圆內接四边形面积公式
14. 基础数论公式
三. 大数模板
四. 数论算法
1. Greatest Common Divisor最大公约数
2. Prime素数判断
3. Sieve Prime素数筛法
4. Module Inverse模逆元
5. Extended Euclid扩展欧几里德算法
6. Modular Linear Equation模线性方程(同余方程)
7. Chinese Remainder Theorem中国余数定理
五. 图论算法
1. 最小生成树(Kruscal算法)
2. 最小生成树(Prim算法)
3. 单源最短路径(Bellman-ford算法)
4. 单源最短路径(Dijkstra算法)
5. 全源最短路径(Folyd算法)
6. 拓扑排序
7. 网络预流和最大流
8. 网络最小费用最大流
9. 网络最大流(高度标号预流推进)
10. 最大团
11. 最大二分图匹配(匈牙利算法)
六. 几何算法
1. 几何模板
2. 球面上两点最短距离
3. 三点求圆心坐标
七. 专题讨论
1. 树状数组
2. 字典树
3. 后缀树
4. 线段树
5. 并查集
6. 二叉堆
7. 逆序数(归并排序)
8. 树状DP
9. 欧拉路
10. 八数码
11. 高斯消元法
12. 字符串匹配(KMP算法)
13. 全排列,全组合
第一章常用函数和STL
一. 常用函数
#include <stdio.h>
int getchar( void ); //读取一个字符, 一般用来去掉无用字符
char *gets( char *str ); //读取一行字符串
#include <stdlib.h>
void * malloc( size_t size ); //动态内存分配, 开辟大小为 size 的空间
void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) ); //快速排序
Sample:
int compare_ints( const void* a, const void* b )
{
int* arg1 = (int*) a; int* arg2 = (int*) b;
if( *arg1 < *arg2 ) return -1;
else if( *arg1 == *arg2 ) return 0;
else return 1;
}
int array[] = { -2, 99, 0, -743, 2, 3, 4 }; int array_size = 7;
qsort( array, array_size, sizeof(int), compare_ints );
#include <math.h>
//求反正弦, arg∈[-1, 1], 返回值∈[-pi/2, +pi/2]
double asin( double arg );
//求正弦, arg为弧度, 弧度=角度*Pi/180.0, 返回值∈[-1, 1]
double sin( double arg );
//求e的arg次方
double exp( double arg );
//求num的对数, 基数为e
double log( double num );
//求num的根
double sqrt( double num );
//求base的exp次方
double pow( double base, double exp );
#include <string.h>
//初始化内存, 常用来初始化数组
void* memset( void* buffer, int ch, size_t count );
memset( the_array, 0, sizeof(the_array) );
//printf是它的变形, 常用来将数据格式化为字符串
int sprintf( char *buffer, const char *format, ... );
sprintf(s, "%d%d", 123, 4567); //s="1234567"
//scanf是它的变形, 常用来从字符串中提取数据
int sscanf( const char *buffer, const char *format, ... );
Sample:
char result[100]="24 hello", str[100]; int num;
sprintf( result, "%d %s", num,str );//num=24;str="hello" ;
//字符串比较, 返回值<0代表str1<str2, =0代表str1=str2, >0代表str1>str2
int strcmp( const char *str1, const char *str2 );
二. 常用STL
[标准container概要]
vector<T> 大小可变的向量, 类似数组的用法, 容易实现删除
list<T> 双向链表
queue<T> 队列, empty(), front(), pop(), push()
stack<T> 栈, empty(), top(), pop(), push()
priority_queue<T> 优先队列, empty(), top(), pop(), push()
set<T> 集合
map<key,val> 关联数组, 常用来作hash映射
[标准algorithm摘录]
for_each() 对每一个元素都唤起(调用)一个函数
find() 查找第一个能与引数匹配的元素
replace() 用新的值替换元素, O(N)
copy() 复制(拷贝)元素, O(N)
remove() 移除元素
reverse() 倒置元素
sort() 排序, O(N log(N))
partial_sort() 部分排序
binary_search() 二分查找
merge() 合并有序的序列, O(N)
[C++ String摘录]
copy() 从别的字符串拷贝
empty() 判断字符串是否为空
erase() 从字符串移除元素
find() 查找元素
insert() 插入元素
length() 字符串长度
replace() 替换元素
substr() 取子字符串
swap() 交换字符串
第二章重要公式与定理
- Fibonacci Number
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610 …
Formula:
- 2. Lucas Number
1, 3, 4, 7, 11, 18, 29, 47, 76, 123...
Formula:
- 3. Catalan Number
1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012…
Formula:
Application:
1) 将 n + 2 边形沿弦切割成 n个三角形的不同切割数
Sample:
n = 2;
n = 3;
2) n + 1个数相乘, 给每两个元素加上括号的不同方法数
Sample:
n = 2; (1 (2 3)), ((1 2) 3)
n = 3; (1 (2 (3 4))), (1 ((2 3) 4)) , ((1 2) (3 4)), ((1 (2 3)) 4), (((1 2) 3) 4)
3) n 个节点的不同形状的二叉树数(严《数据结构》P.155)
4) 从n * n 方格的左上角移动到右下角不升路径数
Sample:
n = 2;
n = 3;
- 4. Stirling Number(Second Kind)
S(n, m)表示含n个元素的集合划分为m个集合的情况数
或者是n个有标号的球放到m 个无标号的盒子中, 要求无一为空, 其不同的方案数
Formula:
Special Cases:
- 5. Bell Number
n 个元素集合所有的划分数
Formula:
- 6. Stirling's Approximation
- 7. Sum of Reciprocal Approximation
EulerGamma = 0.57721566490153286060651209;
- 8. Young Tableau
Young Tableau(杨式图表)是一个矩阵, 它满足条件:
如果格子[i, j]没有元素, 则[i+1, j]也一定没有元素
如果格子[i, j]有元素a[i, j],则[i+1, j]要么没有元素, 要么a[i+1, j] > a[i, j]
Y[n]代表n个数所组成的杨式图表的个数
Formula:
Sample:
n = 3;
- 9. 整数划分
将整数n分成k份, 且每份不能为空, 任意两种分法不能相同
1) 不考虑顺序
for(int p=1; p<=n ;p++)
for(int i=p; i<=n ;i++)
for(int j=k; j>=1 ;j--)
dp[i][j] += dp[i-p][j-1];
cout<< dp[n][k] <<endl;
2) 考虑顺序
dp[i][j] = dp[i-k][j-1]; (k=1..i)
3) 若分解出来的每个数均有一个上限m
dp[i][j] = dp[i-k][ j-1]; (k=1..m)
- 10. 错排公式
- 11. 三角形内切圆半径公式
- 12. 三角形外接圆半径公式
- 13. 圆內接四边形面积公式
- 14. 基础数论公式
1) 模取幂
2) n的约数的个数
若n满足, 则n的约数的个数为
第三章大数模板
/**** **** **** **** **** ****
* Function Name : BigNumber
* Description : BigNumber's HPC
* Author : HuangWei
* Last Edited : 07.4.11
**** **** **** **** **** ****/
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <algorithm>
#define BASE 1000 // 基数
#define DIG 1100 // 存储
using namespace std;
class BigNumber
{
private:
int data[DIG]; //
数据区
int len; // 记录长度
public:
BigNumber() {len=1;memset(data,0,sizeof(data));data[0]=1;}
BigNumber(int); // 输入默认十进制
BigNumber(char*);
BigNumber(const BigNumber &);
// 类型转换
BigNumber & Num_BNum(int); //把一个整数转换成BigNumber型的
BigNumber & Str_BNum(char*); //把一个字符串类型的转换成BigNumber型的
int Int();
string Str();
// HPC
BigNumber & Add(const BigNumber &);
BigNumber & Sub(const BigNumber &);
BigNumber & Mul(const BigNumber &);
BigNumber & Div(int);
BigNumber & Mod(int);
BigNumber & operator=(const BigNumber &);
int Bigger(const BigNumber &) const;
BigNumber operator + (const BigNumber &);
BigNumber operator - (const BigNumber &);
BigNumber operator * (const BigNumber &);
BigNumber operator / (int);
BigNumber operator % (int);
BigNumber & operator += (const BigNumber &);
BigNumber & operator -= (const BigNumber &);
BigNumber & operator *= (const BigNumber &);
BigNumber & operator /= (int);
BigNumber & operator %= (int);
};
BigNumber & BigNumber::Num_BNum(int b)
{
len=1; memset(data,0,sizeof(data));
data[0] = 1;
if(b < 0) {
b = -b;
data[0] = -1;
}
while(b > 0) {
data[ len++ ] = b % BASE;
b /= BASE;
}
return *this;
}
BigNumber & BigNumber::Str_BNum(char* sb)
{
int t=0, d=1, b=0, slen=strlen(sb), i;
len=1; memset(data,0,sizeof(data));
data[0] = 1;
if(sb[0] == '-') data[0] = -1, b=1;
for(i=slen-1; i>=b ;i--) {
while(t >= BASE || d > BASE) {
data[ len++ ] = t % BASE;
t /= BASE;
d = 10;
}
t += (sb[i]-'0') * d;
d *= 10;
}
while(t > 0) {
data[ len++ ] = t % BASE;
t /= BASE;
}
return *this;
}
int BigNumber::Int()
{
istringstream sin;
int v;
sin.str( this->Str() );
sin >> v;
return v;
} //这个函数的用法还是第一次看到,没看懂
string BigNumber::Str()
{
int i,base_len=0;
ostringstream sout;
if(len == 1) {
sout << '0';
//sout << endl;
return sout.str();
}
if(data[0] < 0) sout << "-";
sout << data[len-1];
i = BASE;
while(i > 1) {
base_len++;
i /= 10;
}
for(i=len-2; i>0 ;i--) {
sout.width(base_len);
sout.fill('0');
sout << data[i];
}
//sout << endl;
return sout.str();
} //这个函数也没有看懂
BigNumber::BigNumber(int b)
{this->Num_BNum(b);}
BigNumber::BigNumber(char* sb)
{this->Str_BNum(sb);}
// -1 a<b, 0 a==b, 1 a>b
BigNumber::BigNumber(const BigNumber & b)
{len = b.len; memcpy(data,b.data,sizeof(data));}
int BigNumber::Bigger(const BigNumber & b) const
{
int i,flag;
if(data[0] ==1 && b.data[0] ==1) flag = 1;
else if(data[0] ==1 && b.data[0] ==-1) return 1;
else if(data[0] ==-1 && b.data[0] ==1) return -1;
else flag = -1;
if(len > b.len) return flag;
else if(len == b.len) {
for(i=len-1; i>0 ;i--)
if(data[i] > b.data[i]) return flag;
}
if(i == 0) return 0;
return -flag;
} //比较函数
BigNumber & BigNumber::Add(const BigNumber & b)
{
int i;
if(data[0] * b.data[0] != 1) {
data[0] = -data[0];
Sub(b);
data[0] = -data[0];
return *this;
}
len= len > b.len ? len : b.len;
for(i=1; i<len ;i++) {
data[i] += b.data[i];
if(data[i] >= BASE) {
data[i+1]++;
data[i] -= BASE;
}
}
if(data[i] > 0) len = i+1;
return *this;
} //加上b这个大数
BigNumber & BigNumber::Sub(const BigNumber & b)
{
int i;
if(data[0] * b.data[0] != 1) {
data[0] = -data[0];
Add(b);
data[0] = -data[0];
return *this;
}
len= len > b.len ? len : b.len;
for(i=1; i<len ;i++) {
data[i] -= b.data[i];
if(data[i] < 0) {
data[i+1]--;
data[i] += BASE;
}
}
if(data[len] < 0) {
for(i=0; i<=len ;i++)
data[i] = -data[i];
for(i=1; i<len ;i++)
if(data[i] < 0) {
data[i+1]--;
data[i] += BASE;
}
}
while(data[len-1] == 0) len--;
return *this;
}
BigNumber & BigNumber::Mul(const BigNumber & b)
{
BigNumber bt;
int i,j,up;
int temp,temp1;
bt.data[0] = data[0] * b.data[0];
for(i=1; i<len ;i++) {
up = 0;
for(j=1; j<b.len ;j++) {
temp = data[i] * b.data[j] + bt.data[i+j-1] + up;
if(temp >= BASE) {
temp1 = temp % BASE;
up = temp / BASE;
bt.data[i+j-1] = temp1;
}
else {
up = 0;
bt.data[i+j-1] = temp;
}
}
if(up != 0) bt.data[i+j-1] = up;
}
bt.len = i+j;
while(bt.data[bt.len-1] == 0) bt.len--;
*this=bt;
return *this;
}
BigNumber & BigNumber::Div(int b)
{
BigNumber bt;
int i,down = 0;
if(b < 0) bt.data[0] = -data[0] , b = -b;
else bt.data[0] = data[0];
for(i=len-1; i>=1 ;i--) {
bt.data[i] = (data[i] + down * BASE) / b;
down = data[i] + down * BASE - bt.data[i] * b;
}
bt.len = len;
while(bt.data[bt.len-1] == 0) bt.len--;
*this=bt;
return *this;
}
BigNumber & BigNumber::Mod(int b)
{
int temp = 0, up = 0, i;
for(i=len-1; i>=1 ;i--) {
temp = data[i];
temp += up * BASE;
up = temp % b;
}
if(data[0] < 0) up = -up;
*this = up;
return *this;
}
BigNumber & BigNumber::operator = (const BigNumber & b)
{len = b.len; memcpy(data,b.data,sizeof(data)); return *this;}
BigNumber BigNumber::operator + (const BigNumber & b)
{BigNumber bt=*this; return bt.Add(b);}
BigNumber BigNumber::operator - (const BigNumber & b)
{BigNumber bt=*this; return bt.Sub(b);}
BigNumber BigNumber::operator * (const BigNumber & b)
{BigNumber bt=*this; return bt.Mul(b);}
BigNumber BigNumber::operator / (int b)
{BigNumber bt=*this; return bt.Div(b);}
BigNumber BigNumber::operator % (int b)
{BigNumber bt=*this; return bt.Mod(b);}
BigNumber & BigNumber::operator += (const BigNumber & b)
{return this->Add(b);}
BigNumber & BigNumber::operator -= (const BigNumber & b)
{return this->Sub(b);}
BigNumber & BigNumber::operator *= (const BigNumber & b)
{return this->Mul(b);}
BigNumber & BigNumber::operator /= (int b)
{return this->Div(b);}
BigNumber & BigNumber::operator %= (int b)
{return this->Mod(b);}
第四章数论算法
- 1. Greatest Common Divisor最大公约数
int GCD(int x, int y)
{
int t;
while(y > 0) {
t = x % y;
x = y;
y = t;
}
return x;
}
- 2. Prime素数判断
bool is_prime(int u)
{
if(u == 0 || u == 1) return false;
if(u == 2) return true;
if(u%2 == 0) return false;
for(int i=3; i <= sqrt(u) ;i+=2)
if(u%i==0) return false;
return true;
}
- 3. Sieve Prime素数筛法
const int M = 1000; // M : size
bool mark[M]; // true : prime number
void sieve_prime()
{
memset(mark, true, sizeof(mark));
mark[0] = mark[1] = false;
for(int i=2; i <= sqrt(M) ;i++) {
if(mark[i]) {
for(int j=i*i; j < M ;j+=i)
mark[j] = false;
}
}
}
- 4. Module Inverse模逆元
// ax ≡ 1 (mod n)
int Inv(int a, int n)
{
int d, x, y;
d = extended_euclid(a, n, x, y);
if(d == 1) return (x%n + n) % n;
else return -1; // no solution
}
- 5. Extended Euclid扩展欧几里德算法
//如果GCD(a,b) = d, 则存在x, y, 使d = ax + by
// extended_euclid(a, b) = ax + by
int extended_euclid(int a, int b, int &x, int &y)
{
int d;
if(b == 0) {x = 1; y = 0; return a;}
d = extended_euclid(b, a % b, y, x);
y -= a / b * x;
return d;
}
- 6. Modular Linear Equation模线性方程(同余方程)
//如果GCD(a, b)不能整除c, 则ax + by = c 没有整数解
// ax ≡ b (mod n) n > 0
//上式等价于二元一次方程ax – ny = b
void modular_linear_equation(int a, int b, int n)
{
int d, x, y, x0;
d = extended_euclid(a, n, x, y);
if( b%d == 0) {
x0 = ( x*(b/d) ) % n;
// x0 : basic
solution
int ans = n;
for(int i=0; i < d ;i++) {
ans = ( x0 + i*(n/d) ) % n;
cout << ans << endl;
}
}
else cout << "no solution" << endl;
}
- 7. Chinese Remainder Theorem中国余数定理
// x ≡ b[i] (mod w[i]),
i∈[1, len-1]
// 前提条件w[i] > 0, 且w[]中任意两个数互质
int chinese_remainder(int b[], int w[], int len)
{
int i, d, x, y, m, n;
x = 0; n = 1;
for(i=0; i < len ;i++) n *= w[i];
for(i=0; i < len ;i++) {
m = n / w[i] ;
d = extended_euclid(w[i], m, x, y);
x = (x + y*m*b[i]) % n;
}
return (n + x%n) % n;
}
第五章图论算法
- 1. 最小生成树(Kruscal算法)
/**** **** **** **** **** ****
* Function Name : 最小生成树(Kruscal算法)
* Description : ZJU 1203 Swordfish O(E*LogE)
**** **** **** **** **** ****/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
struct struct_edges
{
int bv,tv; //bv 起点 tv 终点
double w; //权值
};
struct_edges edges[10100]; //边集
struct struct_a
{
double x;
double y;
};
struct_a arr_xy[101];
int point[101],n,e; //n 顶点数, e 边数(注意是无向网络)
double sum;
int kruscal_f1(int point[], int v)
{
int i = v;
while(point[i] > 0) i = point[i];
return i;
}
bool UDlesser(struct_edges a, struct_edges b)
{return a.w < b.w;}
void kruscal() //只需要准备好n,e,递增的边集edges[]即可使用
{
int v1,v2,i,j;
for(i=0; i<n ;i++) point[i]=0;
i = j = 0;
while(j<n-1 && i<e) {
v1 = kruscal_f1(point, edges[i].bv);
v2 = kruscal_f1(point, edges[i].tv);
if(v1 != v2) {
sum += edges[i].w; //注意sum初始为0
point[v1]=v2;
j++;
}
i++;
}
}
int main()
{
int k,i,j;
cin>>n;
k=0;
while(n != 0) {
sum=0;
k++;
for(i=0; i<n ;i++)
cin>>arr_xy[i].x>>arr_xy[i].y;
e=0;
for(i=0; i<n ;i++) //从0开始计数
for(j=i+1; j<n ;j++) //注意是无向网络
{
if(i == j) continue;
edges[e].bv=i;
edges[e].tv=j;
edges[e].w=sqrt((arr_xy[i].x-arr_xy[j].x)*(arr_xy[i].x-arr_xy[j].x)+(arr_xy[i].y-arr_xy[j].y)*(arr_xy[i].y-arr_xy[j].y));
e++;
}
sort(edges,edges+e,UDlesser); //得到一个递增的边集,注意是从0开始计数
kruscal();
printf("Case #%d:
",k); //cout<<"Case #"<<k<<":"<<endl;
printf("The minimal distance is: %.2f
",sum); //输出sum
cin>>n;
if(n != 0) printf("
");
}
}
- 2. 最小生成树(Prim算法)
/**** **** **** **** **** ****
* Function Name : 最小生成树(Prim算法)
* Description : ZJU 1203 Swordfish O(N^2)
**** **** **** **** **** ****/
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
double sum, arr_list[101][101], min;
int i, j, k=0, n;
struct struct_a
{
float x;
float y;
};
struct_a arr_xy[101];
struct struct_b
{
int point;
float lowcost;
};
struct_b closedge[101];
void prim(int n) //prim 需要准备:n顶点数 arr_list[][]顶点的邻接矩阵也是从0开始计数
{
int i,j,k;
k=0;
for(j=0; j<n ;j++) {
if(j != k) {
closedge[j].point = k;
closedge[j].lowcost = arr_list[k][j];
}
}
closedge[k].lowcost=0;
for(i=0; i<n ;i++) {
min=10000;
for(j=0; j<n ;j++) {
if (closedge[j].lowcost != 0 && closedge[j].lowcost < min) {
k = j;
min = closedge[j].lowcost;
}
}
sum += closedge[k].lowcost; //不要改成sum+=min; sum即为所求值
closedge[k].lowcost = 0;
for(j=0; j<n ;j++) {
if(arr_list[k][j] < closedge[j].lowcost) {
closedge[j].point = k;
closedge[j].lowcost = arr_list[k][j];
}
}
}
}
/*
arr_list[][]= Wij 如果Vi, Vj有边
0 如果i=j
无限大 如果没有边
*/
int main()
{
cin>>n;
while(n != 0) {
sum=0;
k++;
for(i=0; i<n ;i++)
cin>>arr_xy[i].x>>arr_xy[i].y;
for(i=0; i<n ;i++)
for(j=0; j<n ;j++) //得到邻接矩阵arr_list[][]
arr_list[i][j]=arr_list[j][i]=sqrt((arr_xy[i].x-arr_xy[j].x)*(arr_xy[i].x-arr_xy[j].x)+(arr_xy[i].y-arr_xy[j].y)*(arr_xy[i].y-arr_xy[j].y));
prim(n);
cout<<"Case #"<<k<<":"<<endl;
printf("The minimal distance is: %.2f
",sum);
cin>>n;
if(n!=0) printf("
");
}
}
- 3. 单源最短路径(Bellman-ford算法)
/**** **** **** **** **** ****
* Function Name : 单源最短路径(Bellman-ford算法)
* Description : 可允许有负权
**** **** **** **** **** ****/
#include <stdio.h>
#define MAX 100
#define MAXNUM 1000000
typedef struct graphnode
{
int vexnum; //顶点数
int arcnum; //边数
int gra[MAX][MAX]; //图
}Graph;
Graph *G;
//arc数组中存储的第一个顶点到其他顶点的最短路径
//结果存在dis数组中
int dis[MAX];
int arc[MAX][MAX];
void bellman(Graph *G)
{
int i,j;
bool sign;
for(i=0; i < G->vexnum ;i++) dis[i]=MAXNUM;
dis[1] = 0;
sign = true;
for(i=1; i < G->vexnum ;i++) {
sign = false;
for(j=0; j < G->arcnum ;j++) {
if(dis[ arc[j][0] ] < MAXNUM
&& dis[ arc[j][1] ] > dis[ arc[j][0] ] + G->gra[ arc[j][0] ][ arc[j][1] ])
{
dis[ arc[j][1] ]=dis[ arc[j][0] ] + G->gra[ arc[j][0] ][ arc[j][1] ];
sign = true;
}
}
}
return;
}
- 4. 单源最短路径(Dijkstra算法)
/**** **** **** **** **** ****
* Function Name : 单源最短路径 (Dijkstra算法)
* Description : 贪心, O(N^2), 不能有负权
**** **** **** **** **** ****/
int matrix[200][200],n; //matrix[][], 30000表示无限大,即无边.否则为有边,其值为边的权值
void Dijkstra(int x,int y) //起点Vx 终点Vy
{
int i,j,k,path[40000],mark[40000];
int min,dist[40000];
for(i=1;i<=n;i++) {
mark[i] = 0;
dist[i] = matrix[x][i];
path[i] = x;
}
mark[x] = 1;
do {
min=30000;
k=0;
for(i=1;i<=n;i++)
if(mark[i]==0 && dist[i]<min) {
min = dist[i];
k = i;
}
if(k) {
mark[k] = 1;
for(i=1;i<=n;i++)
if(matrix[k][i]<30000 && min+matrix[k][i]<dist[i]) {
dist[i] = min + matrix[k][i];
path[i] = k;
}
}
}while(k);
cout<<dist[y]<<endl; //dist[y] 的值就是从Vx 到 Vy 的最短路径值
//如果希望得到路径,加入如下代码:
do {
cout<<k<<"<--";
k = path[k];
}while(k!=x);
cout<<x<<endl;
}
- 5. 全源最短路径(Folyd算法)
/**** **** **** **** **** ****
* Function Name : 全源最短路径(Folyd算法)
* Description : DP, O(N^3)
**** **** **** **** **** ****/
//初始化
//min_graph[i][j]=graph[i][j];
//path[i][j]=j;
void Floyd()
{
int i,j,k;
for(k=0;k<vertex_number;k++) {
for(i=0;i<vertex_number;i++) {
for(j=0;j<vertex_number;j++) {
if((graph[i][k]==-1) || (graph[k][j]==-1)) continue;
if((min_graph[i][j]==-1) || (min_graph[i][j] > graph[i][k]+graph[k][j]))
{
min_graph[i][j] = graph[i][k]+graph[k][j]; /*最短路径值*/
path[i][j] = k; /*最短路径*/
}
}
}
}
}
- 6. 拓扑排序
/**** **** **** **** **** ****
* Function Name : 拓扑排序
**** **** **** **** **** ****/
//degree[] 每个结点的入度
//f[] 每个结点所在的层
void Toplogical_sort()
{
int i,j;
bool p=true;
top=0;
while(p) {
p=false;
top++;
for(i=1;i<=n;i++)
if(degree[i]==0) {
p=true;
f[i]=top;
}
for(i=1;i<=n;i++)
if(f[i]==top) {
for(j=1;j<=n;j++)
if(map[i][j]) degree[j]--;
degree[i]=-1;
}
}
top--;
}
- 7. 网络预流和最大流
int rel[1000][10000]; //全局变量
int pre[1000];
//计算网络流
//如果是二分图的匹配, 可以先对其进行网络预流以简化后续的查找
int pre_flow(int n,vector<int> * v)
{
int ret = 0;
int i,j,t,t1;
for(i = 0 ; i < v[0].size() ; i++){
t = v[0][i]; //t是与节点0相邻接的点
for(j = 0 ; j < v[t].size() ; j++){
t1 = v[t][j]; //与t相邻接的点
if(rel[t1][n - 1] > 0){
ret++;
rel[0][t]--, rel[t][0]++;
rel[t][t1]--, rel[t1][t]++;
rel[t1][n - 1]--, rel[n - 1][t1]++;
break;
}
}
}
return ret;
}
/*
网络中求最大流
参数含义: n代表网络中节点数,第0节点为源点, 第n-1节点为汇点
rel是个二维数组, rel[i][j]代表从节点i到节点j的流量
v[]是一个节点数组, v[i]包含与节点i相邻接的所有节点
返回值: 最大流量
*/
int max_flow(int n,vector<int> * v)
{
int ret = 0,i;
int t,t1,tm;
queue<int> q;
const int Infinite = 2000000000;
while(1){
for(t = 0 ; t < n ; t++) pre[t] = -1;
while(!q.empty()) q.pop();
q.push(0);
while(!q.empty()){ //find a augmenting path using breath-first search
t = q.front();
q.pop();
if(t == n - 1) break; //到达汇点
for(i = 0 ; i < v[t].size() ; i++){ //对于t相邻接的所有点查找可行路径
t1 = v[t][i];
if(rel[t][t1] > 0 && pre[t1] == -1){
pre[t1] = t;
q.push(t1);
}
}
}
if(q.empty() && t != n - 1) break;
tm = Infinite; //此处寻找路径最小值在二分图中可省略
while(t != 0){ //find the minimal num in the path
t1 = pre[t];
if(rel[t1][t] < tm) tm = rel[t1][t];
t = t1;
}
// tm = 1; //二分图中
t = n - 1;
while(t != 0){ //change the relation
t1 = pre[t];
rel[t1][t] -= tm;
rel[t][t1] += tm;
t = t1;
}
ret += tm;
}
return ret;
}
- 8. 网络最小费用最大流
/**** **** **** **** **** ****
网络中最小费用最大流
参数含义: np代表网络中的总节点数, v是网络节点的邻接表
cost为最后求得的最小费用, mf为求得的最大流
算法: 初始最小费用及最大流均为0,不断寻找可增广路
增广路对应的单位费用最小并且可流
修改残留网络及cost,mf.
直到无可增广路为止。
**** **** **** **** **** ****/
const int Max = 200000000;
vector<int> v[110]; //存储每个节点的邻接点
int flow[110][110]; //flow[i][j]代表由i节点到j节点的可行流量
int fcost[110][110]; //fcost[i][j]代表由i节点到j节点的单位流量费用
int ct[110]; //ct[i]代表单位容量到达i节点的最小费用
int pre[110]; //可行节点的前驱节点
void min_cost_max_flow(int np,const vector<int> * v,int & cost,int & mf)
{
int t,t1,tm,i,j;
bool out;
cost = 0,mf = 0;
while(1){
for(i = 0 ; i < np ; i++) pre[i] = -1,ct[i] = Max;
ct[0] = 0;
while(1){
out = false;
for(i = 0 ; i < np ; i++){
for(j = 0 ; j < v[i].size() ; j++){
t = v[i][j];
if(flow[i][t] > 0 && ct[i] != Max && ct[i] + fcost[i][t] < ct[t]){
out = true;
ct[t] = ct[i] + fcost[i][t];
pre[t] = i;
}
}
}
if(!out) break;
}
if(ct[np - 1] != Max){
t = np - 1;
tm = Max; //此处寻找流量最小值
while(t != 0){ //find the minimal num in the path
t1 = pre[t];
if(flow[t1][t] < tm) tm = flow[t1][t];
t = t1;
}
mf += tm; //流量增加
t = np - 1;
while(t != 0){ //change the relation
t1 = pre[t];
flow[t1][t] -= tm;
flow[t][t1] += tm;
cost += tm * fcost[t1][t]; //费用增加
t = t1;
}
}
else break;
}
}
- 9. 网络最大流(高度标号预流推进)
/*
函数接口: int Relabel_To_Front(int s,int d)
参数含义: s为源点,d为汇点
返回值 : 网络最大流
调用函数前的初始化工作:ver置为网络中节点的个数,c[i][j]代表节点i到
节点j的流量,vl[i]存放i与相邻的所有节点
其它全局变量均初始化为零
*/
const int VEX = 405; //网络中顶点数
const int HMAX = 810; //最大高度的定义,只要大于顶点的2倍就可以了
int f[VEX][VEX]; //流量
int c[VEX][VEX]; //边最大容量
int h[VEX]; //节点高度
int e[VEX]; //节点容量
int ver; //节点数目
vector<int> vl[VEX]; //邻接表,vl[i]存放与i相邻的节点
void Push(int u,int v) //流推进,由节点u推向v
{
int cf = c[u][v] - f[u][v]; //u,v边的容量
int d = e[u] < cf ? e[u] : cf;
f[u][v] += d;
f[v][u] = -f[u][v];
e[u] -= d;
e[v] += d;
}
void Relabel(int u) //对u重新标号
{
int i,t,cf;
int hmin = HMAX;
for(i = 0 ; i < vl[u].size() ; i++){ //寻找相邻最低点
t = vl[u][i];
cf = c[u][t] - f[u][t];
if(cf > 0 && h[u] <= h[t] && h[t] < hmin)
hmin = h[t];
}
h[u] = hmin + 1;
}
void Init_Preflow(int s) //初始化网络流,s为源点
{
int i;
int u;
h[s] = ver; //初始化高度
for(i = 0 ; i < vl[s].size() ; i++){
u = vl[s][i];
f[s][u] = c[s][u];
f[u][s] = -c[s][u];
e[u] = c[s][u];
e[s] -= c[s][u];
}
}
void Discharge(int u)
{
int i = 0;
int cf,v;
if(vl[u].size() == 0) return;
while(e[u] > 0){
if(i < vl[u].size()) {
v = vl[u][i];
cf = c[u][v] - f[u][v];
}
if(i >= vl[u].size()){
Relabel(u);
i = 0;
}
else if(cf > 0 && h[u] == h[v] + 1)
Push(u,v);
else
i++;
}
}
int Relabel_To_Front(int s,int d) //s为源点,d为汇点
{
int u,i,old_h;
list<int> l;
list<int>::iterator iter;
Init_Preflow(s);
iter = l.begin();
for(i = 0 ; i < ver ; i++){
if(i != s && i != d)
l.insert(iter,i);
}
iter = l.begin();
while(iter != l.end()){
u = *iter;
old_h = h[u];
Discharge(u);
if(h[u] > old_h){
l.erase(iter);
l.insert(l.begin(),u);
iter = l.begin();
}
iter++;
}
return e[ver - 1];
}
- 10. 最大团
/**** **** **** **** **** ****
* Function Name : 最大团
* Description : ZJU 1492 Maximum Clique
* 团: 指G的一个完全子图, 该子图不包含在任何其他的完全子图当中
* 最大团: 指其中包含顶点最多的团
**** **** **** **** **** ****/
#include<stdio.h>
#include<string.h>
int joint[50][50];
int Size, MAX, DP[50];
bool find;
//返回-1表示邻接顶点集已经为空集,否则返回第一个公共顶点
int reachEnd(int start, int sets[])
{
int lp;
for(lp=start; lp<Size ;lp++)
if(sets[lp]) return lp;
return -1;
}
void DFS(int Visit[], int start, int depth)
{
int loop, first, sets[50], SET[50];
memcpy(sets,Visit,Size*4);
memcpy(SET,Visit,Size*4);
if(( first=reachEnd(start,sets) ) == -1) {
if(depth > MAX) {
MAX = depth;
find = true;
}
return ;
}
while(first != -1) {
if(depth + Size - start <= MAX)//不可能找到最优解
return ;
if(depth + DP[first] <= MAX)//不可能找到最优解
return;
sets[first] = 0;
SET[first] = 0;//从邻接顶点集中清除first顶点
for(loop=first+1; loop < Size ;loop++) //合并邻接顶点集
if(SET[loop]==1 && joint[first][loop]==1) sets[loop]=1;
else sets[loop]=0;
DFS(sets,first,depth+1);
if(find) return ;
first = reachEnd(first,SET);//更新接点
}
}
int main()
{
int loop, lp, Visit[50];
while(scanf("%d",&Size)!=EOF && Size!=0) {
for(loop=0; loop < Size ;loop++)
for(lp=0; lp < Size ;lp++)
scanf("%d",joint[loop]+lp);
MAX=0;
for(loop=Size-1; loop >= 0 ;loop--) {
find=false;
memcpy(Visit, joint[loop], Size*4);
DFS(Visit, loop, 1);
DP[loop] = MAX;
}
printf("%d
",DP[0]);
}
}
- 11. 最大二分图匹配(匈牙利算法)
/**** **** **** **** **** ****
* Function Name : 最大二分图匹配(匈牙利算法)
* Description : HDOJ 2063 过山车
* 二分图: 指所有顶点分成集合M和N, M或N中任意两个在同一集合中的点互不相连
* 匹配: 一组边顶点分别在两个集合中, 并且任意两条边都没有相同顶点
* 最大匹配: 所能得到的最大的边的个数
**** **** **** **** **** ****/
#include<cstdio>
#include<memory>
#include<vector>
using namespace std;
const int Max=1100;
vector< vector<int> > Bmap;
int n, m, k, nm;
int mark[Max];
bool flag[Max];
bool dfs(int pos)
{
int i, pre, tp;
for(i=0; i < Bmap[pos].size() ;i++) {
tp = Bmap[pos][i];
if( !flag[tp] ) {
flag[tp] = true;
pre = mark[tp];
mark[tp] = pos;
if(pre==-1 || dfs(pre)) return true;
mark[tp] = pre;
}
}
return false;
}
inline int Max_Match()
{
int mmax = 0, i;
for(i=1; i <= m ;i++) {
memset(flag,0,sizeof(flag));
if( dfs(i) ) mmax++;
}
return mmax;
}
int main()
{
int i, j, id, id2;
while(scanf("%d", &k)==1 && k) {
scanf("%d%d",&m, &n);
nm = n + m;
Bmap.clear(); Bmap.resize(nm+10);
memset(mark,-1,sizeof(mark));
for(j=0; j < k ;j++) {
scanf("%d %d", &id, &id2);
id2 += m;
Bmap[id].push_back(id2);
}
printf("%d
", Max_Match());
}
}
第六章几何算法
/****************************************
* COMPUTATIONAL GEOMETRY ROUTINES
* WRITTEN BY : LIU Yu (C) 2003
****************************************/
// 叉乘
// 两个点的距离
// 点到直线距离
// 返回直线 Ax + By + C =0 的系数
// 线段
// 圆
// 两个圆的公共面积
// 矩形
// 根据下标返回多边形的边
// 两个矩形的公共面积
// 多边形 ,逆时针或顺时针给出x,y
// 多边形顶点
// 多边形的边
// 多边形的周长
// 判断点是否在线段上
// 判断两条线断是否相交,端点重合算相交
// 判断两条线断是否平行
// 判断两条直线断是否相交
// 直线相交的交点
// 判断是否简单多边形
// 求多边形面积
// 判断是否在多边形上
// 判断是否在多边形内部
// 点阵的凸包,返回一个多边形
// 最近点对的距离
#include <cmath>
#include <cstdio>
#include <memory>
#include <algorithm>
#include <iostream>
using namespace std;
typedef double TYPE; //把double 定义为TYPE
#define Abs(x) (((x)>0)?(x):(-(x))) //用Abs()这个宏定义一个绝对值函数
#define Sgn(x) (((x)<0)?(-1):(1)) //取相反数
#define Max(a,b) (((a)>(b))?(a):(b))
//取大值
#define Min(a,b) (((a)<(b))?(a):(b))
//取小值
#define Epsilon 1e-10
#define Infinity 1e+10
#define Pi 3.14159265358979323846 //定义几个常量
TYPE Deg2Rad(TYPE deg)
{return (deg * Pi / 180.0);} //把角度制转化为弧度制
TYPE Rad2Deg(TYPE rad)
{return (rad * 180.0 / Pi);} //把弧度制转化为角度制
TYPE Sin(TYPE deg)
{return sin(Deg2Rad(deg));} //对弧度制求正弦
TYPE Cos(TYPE deg)
{return cos(Deg2Rad(deg));} //对弧度制求余弦
TYPE ArcSin(TYPE val)
{return Rad2Deg(asin(val));} //求反正弦
TYPE ArcCos(TYPE val)
{ return Rad2Deg(acos(val));} //求反余弦
TYPE Sqrt(TYPE val)
{ return sqrt(val);}
struct POINT
{
TYPE x;
TYPE y;
TYPE z;
POINT() : x(0), y(0), z(0) {};
POINT(TYPE _x_, TYPE _y_, TYPE _z_ = 0) : x(_x_), y(_y_), z(_z_) {};
};
// cross product of (o->a) and (o->b)
// 叉乘
TYPE Cross(const POINT & a, const POINT & b, const POINT & o)
{return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);} //叉积模板
// planar points' distance
// 两个点的距离
TYPE Distance(const POINT & a, const POINT & b)
{return Sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));}
struct LINE
{
POINT a;
POINT b;
LINE() {};
LINE(POINT _a_, POINT _b_) : a(_a_), b(_b_) {};
}; //直线由两点决定
//点到直线距离
double PointToLine(POINT p0 ,POINT p1 ,POINT p2 ,POINT &cp)
{
double d = Distance(p1 ,p2);
double s = Cross(p1 ,p2 ,p0) / d;
cp.x = p0.x + s*( p2.y-p1.y) / d;
cp.y = p0.y - s*( p2.x-p1.x) / d;
return Abs(s);
}
// 返回直线 Ax + By + C =0 的系数
void Coefficient(const LINE & L, TYPE & A, TYPE & B, TYPE & C)
{
A = L.b.y - L.a.y;
B = L.a.x - L.b.x;
C = L.b.x * L.a.y - L.a.x * L.b.y;
}
void Coefficient(const POINT & p,const TYPE a,TYPE & A,TYPE & B,TYPE & C)
{
A = Cos(a);
B = Sin(a);
C = - (p.y * B + p.x * A);
}
// 线段
struct SEG
{
POINT a;
POINT b;
SEG() {};
SEG(POINT _a_, POINT _b_):a(_a_),b(_b_) {};
};
// 圆
struct CIRCLE
{
TYPE x;
TYPE y;
TYPE r;
CIRCLE() {}
CIRCLE(TYPE _x_, TYPE _y_, TYPE _r_) : x(_x_), y(_y_), r(_r_) {}
}; //圆由圆心和半径组成
POINT Center(const CIRCLE & circle)
{ return POINT(circle.x, circle.y);}
//返回的是一个POINT类型的对象,他这里没有直接设出一个具体的对象,而是直接用类名来实现,也是可以的,而且他这里是把对象传递进来,返回的是他的圆心
//圆的面积
TYPE Area(const CIRCLE & circle)
{ return Pi * circle.r * circle.r;}
//两个圆的公共面积
TYPE CommonArea(const CIRCLE & A, const CIRCLE & B)
{
TYPE area = 0.0;
const CIRCLE & M = (A.r > B.r) ? A : B;
const CIRCLE & N = (A.r > B.r) ? B : A; //按照圆半径的大小来把圆区分开来
TYPE D = Distance(Center(M), Center(N));
if ((D < M.r + N.r) && (D > M.r - N.r))
//判断出两个圆之间的关系是相交的
{
TYPE cosM = (M.r * M.r + D * D - N.r * N.r) / (2.0 * M.r * D);
TYPE cosN = (N.r * N.r + D * D - M.r * M.r) / (2.0 * N.r * D);
TYPE alpha = 2.0 * ArcCos(cosM);
TYPE beta = 2.0 * ArcCos(cosN);
TYPE TM = 0.5 * M.r * M.r * Sin(alpha);
TYPE TN = 0.5 * N.r * N.r * Sin(beta);
TYPE FM = (alpha / 360.0) * Area(M);
TYPE FN = (beta / 360.0) * Area(N);
area = FM + FN - TM - TN;
}//相交圆的公共部分的面积就是通过一个扇形的面积减去三角形积 ,然后把这两个部分相加得到
else if (D <= M.r - N.r)
//如果两圆是内含的关系,那么公共圆的面积就是一个圆的面积
{
area = Area(N);
}
return area;
}
// 矩形
// 矩形的线段
// 2
// --------------- b
// | |
// 3 | | 1
// a ---------------
// 0
struct RECT
{
POINT a; // 左下点
POINT b; // 右上点
RECT() {};
RECT(const POINT & _a_, const POINT & _b_)
{a = _a_; b = _b_;}
};
//根据下标返回多边形的边 (没有看懂是干什么的,或者说是具体怎么操作的)
SEG Edge(const RECT & rect, int idx) //SEG是线段
{
SEG edge;
while (idx < 0) idx += 4;
switch (idx % 4)
{
case 0:
edge.a = rect.a;
edge.b = POINT(rect.b.x, rect.a.y);
break;
case 1:
edge.a = POINT(rect.b.x, rect.a.y);
edge.b = rect.b;
break;
case 2:
edge.a = rect.b;
edge.b = POINT(rect.a.x, rect.b.y);
break;
case 3:
edge.a = POINT(rect.a.x, rect.b.y);
edge.b = rect.a;
break;
default:
break;
}
return edge;
}
TYPE Area(const RECT & rect)
{return (rect.b.x - rect.a.x) * (rect.b.y - rect.a.y);}
// 两个矩形的公共面积
TYPE CommonArea(const RECT & A, const RECT & B)
{
TYPE area = 0.0;
POINT LL(Max(A.a.x, B.a.x), Max(A.a.y, B.a.y));
POINT UR(Min(A.b.x, B.b.x), Min(A.b.y, B.b.y));
//这里很妙,可以直接把相交部分的左下方的点与右上方的点的坐标求出来
if ((LL.x <= UR.x) && (LL.y <= UR.y)) //判断是否两个矩形是否相交
{
area = Area(RECT(LL, UR));
}
return area;
}
// 多边形 ,逆时针或顺时针给出x,y
struct POLY
{
int n; //n个点
TYPE * x; //x,y为点的指针,首尾必须重合
TYPE * y;
POLY() : n(0), x(NULL), y(NULL) {};
POLY(int _n_, const TYPE * _x_, const TYPE * _y_)
{
n = _n_;
x = new TYPE[n + 1];
memcpy(x, _x_, n*sizeof(TYPE));
x[n] = _x_[0];
y = new TYPE[n + 1];
memcpy(y, _y_, n*sizeof(TYPE));
y[n] = _y_[0];
}
};
//多边形顶点
POINT Vertex(const POLY & poly, int idx)
{
idx %= poly.n; //idx应该指的是点的个数
return POINT(poly.x[idx], poly.y[idx]); //由于POLY里面的x,y是指针,那么就相当于是关于多边形顶点的坐标的一个数组,用来记录下所有多边形顶点的坐标信息
}
//多边形的边
SEG Edge(const POLY & poly, int idx)
{
idx %= poly.n;
return SEG(POINT(poly.x[idx], poly.y[idx]),
POINT(poly.x[idx + 1], poly.y[idx + 1]));
}
//多边形的周长
TYPE Perimeter(const POLY & poly)
{
TYPE p = 0.0;
for (int i = 0; i < poly.n; i++)
p = p + Distance(Vertex(poly, i), Vertex(poly, i + 1));
return p;
}
bool IsEqual(TYPE a, TYPE b)
{return (Abs(a - b) < Epsilon);}
//基础的用于判断两个数是否相等
bool IsEqual(const POINT & a, const POINT & b)
{return (IsEqual(a.x, b.x) && IsEqual(a.y, b.y));} //重载一下用于判断两个点是否相等
bool IsEqual(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1); //把直线A的系数返回给A1,B1,C1
Coefficient(B, A2, B2, C2);
return IsEqual(A1 * B2, A2 * B1) &&
IsEqual(A1 * C2, A2 * C1) &&
IsEqual(B1 * C2, B2 * C1);
} //判断两条直线是否相等
// 判断点是否在线段上
bool IsOnSeg(const SEG & seg, const POINT & p)
{
return (IsEqual(p, seg.a) || IsEqual(p, seg.b)) ||
(((p.x - seg.a.x) * (p.x - seg.b.x) < 0 ||
(p.y - seg.a.y) * (p.y - seg.b.y) < 0) &&
(IsEqual(Cross(seg.b, p, seg.a), 0)));
} //前面两个IsEqual是用来说明要判断的点是否就是直线的一个端点,中间两个用于判断这个点是否会在线的延长线上,最后一个用于判断三点是否共线。只有这样才能有效的保证点落在线段上
//判断两条线断是否相交,端点重合算相交
bool IsIntersect(const SEG & u, const SEG & v)
{
return (Cross(v.a, u.b, u.a) * Cross(u.b, v.b, u.a) >= 0) &&
(Cross(u.a, v.b, v.a) * Cross(v.b, u.b, v.a) >= 0) &&
(Max(u.a.x, u.b.x) >= Min(v.a.x, v.b.x)) &&
(Max(v.a.x, v.b.x) >= Min(u.a.x, u.b.x)) &&
(Max(u.a.y, u.b.y) >= Min(v.a.y, v.b.y)) &&
(Max(v.a.y, v.b.y) >= Min(u.a.y, u.b.y));
} //后面的四个比较用于限定线段相交时的一些点坐标之间的关系,只有这样限定后才可以保证线段相交,否则如果没有这四个条件的话,只能保证的是直线的相交。。。
//判断两条线断是否平行
bool IsParallel(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1);
Coefficient(B, A2, B2, C2);
return (A1 * B2 == A2 * B1) &&
((A1 * C2 != A2 * C1) || (B1 * C2 != B2 * C1));
} //只要系数成比例就行
//判断两条直线断是否相交
bool IsIntersect(const LINE & A, const LINE & B)
{return !IsParallel(A, B);} //不平行就相交
//直线相交的交点
POINT Intersection(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1);
Coefficient(B, A2, B2, C2);
POINT I(0, 0);
I.x = - (B2 * C1 - B1 * C2) / (A1 * B2 - A2 * B1);
I.y = (A2 * C1 - A1 * C2) / (A1 * B2 - A2 * B1);
return I;
}
//判断矩形是否在圆内
bool IsInCircle(const CIRCLE & circle, const RECT & rect)
{
return (circle.x - circle.r >= rect.a.x) &&
(circle.x + circle.r <= rect.b.x) &&
(circle.y - circle.r >= rect.a.y) &&
(circle.y + circle.r <= rect.b.y);
}
//判断是否简单多边形
bool IsSimple(const POLY & poly)
{
if (poly.n < 3)
return false;
SEG L1, L2;
for (int i = 0; i < poly.n - 1; i++)
{
L1 = Edge(poly, i); //用Edge取出多边形的边
for (int j = i + 1; j < poly.n; j++)
//用二重循环来对多边形上的边进行一一判断
{
L2 = Edge(poly, j);
if (j == i + 1) //相邻的两条边进行比较
{
if (IsOnSeg(L1, L2.b) || IsOnSeg(L2, L1.a)) return false; //如果第二条直线的右边的点在第一条直线上或者第一条直线的坐边的点缀第二条直线上,说明这个多边形是凹的,不是简单的
}
else if (j == poly.n - i - 1) //同上面的一样,不过这里取出的是左边的相邻直线
{
if (IsOnSeg(L1, L2.a) || IsOnSeg(L2, L1.b)) return false;
}
else
{
if (IsIntersect(L1, L2)) return false; //不相邻的直线但是相交,那么说明非简单
}
} // for j
} // for i
return true;
}
//求多边形面积
TYPE Area(const POLY & poly)
{
if (poly.n < 3) return TYPE(0); //如果边数小于3说明非多边形,没有面积可言
double s = poly.y[0] * (poly.x[poly.n - 1] - poly.x[1]);
for (int i = 1; i < poly.n; i++)
{
s += poly.y[i] * (poly.x[i - 1] - poly.x[(i + 1) % poly.n]);
}
return s/2;
} //把多边形分割成三角形的和,不过这里的面积计算的方法没有看懂
//判断点是否在多边形上
bool IsOnPoly(const POLY & poly, const POINT & p)
{
for (int i = 0; i < poly.n; i++)
{
if (IsOnSeg(Edge(poly, i), p)) return true;
}
return false;
}
//判断点是否在多边形内部
bool IsInPoly(const POLY & poly, const POINT & p)
{
SEG L(p, POINT(Infinity, p.y)); //Infinity==1e+10,L这条直线相当于是过p点且平行于x轴的直线
int count = 0;
for (int i = 0; i < poly.n; i++)
{
SEG S = Edge(poly, i);
if (IsOnSeg(S, p))
{
return false; //如果想让 在poly上则返回 true,则改为true
}//点在多边形的边上,非内部,返回false
if (!IsEqual(S.a.y, S.b.y))
{
POINT & q = (S.a.y > S.b.y)?(S.a):(S.b); //q取这条多边形边上y坐标大的点
if (IsOnSeg(L, q))
{
++count;
}
else if (!IsOnSeg(L, S.a) && !IsOnSeg(L, S.b) && IsIntersect(S, L))
{
++count;
}
}
}
return (count % 2 != 0);
}//这里用count这个计数器的奇偶来判断点是否在多边形内没有看懂
// 点阵的凸包,返回一个多边形 ,Graham-scan算法
POLY ConvexHull(const POINT * set, int n) // 不适用于点少于三个的情况
{
POINT * points = new POINT[n];
memcpy(points, set, n * sizeof(POINT));
TYPE * X = new TYPE[n];
TYPE * Y = new TYPE[n];
int i, j, k = 0, top = 2;
for(i = 1; i < n; i++)
{
if ((points[i].y < points[k].y) ||
((points[i].y == points[k].y) &&
(points[i].x < points[k].x)))
{
k = i;
}
} //找到p0点,一般取y坐标最小,如果y相同则取x坐标最小,即最左边的点
std::swap(points[0], points[k]);
for (i = 1; i < n - 1; i++)
{
k = i;
for (j = i + 1; j < n; j++)
{
if ((Cross(points[j], points[k], points[0]) > 0) ||
((Cross(points[j], points[k], points[0]) == 0) &&
(Distance(points[0], points[j]) < Distance(points[0], points[k]))))
{
k = j;
}
}
std::swap(points[i], points[k]);
}
X[0] = points[0].x; Y[0] = points[0].y;
X[1] = points[1].x; Y[1] = points[1].y;
X[2] = points[2].x; Y[2] = points[2].y;
for (i = 3; i < n; i++)
{
while (Cross(points[i], POINT(X[top], Y[top]),
POINT(X[top - 1], Y[top - 1])) >= 0)
{
top--;
}
++top;
X[top] = points[i].x;
Y[top] = points[i].y;
}
delete [] points;
POLY poly(++top, X, Y);
delete [] X;
delete [] Y;
return poly;
}
//最近点对的距离, Written By PrincessSnow
#define MAXN 100000
POINT pt[MAXN];
bool cmp(POINT n1, POINT n2)
{return (n1.x<n2.x || n1.x==n2.x && n1.y<n2.y);}
double Get(double dis, int mid, int start, int end)
{
int s=mid, e=mid, i, j;
double t;
while(s > start && pt[mid].x - pt[s].x <= dis) s--;
while(e < end && pt[e].x - pt[mid].x <= dis) e++;
for(i=s; i <= e; i++)
for(j=i+1; j <= e && j <= i+7; j++) {
t = Distance(pt[i], pt[j]);
if(t < dis) dis=t;
}
return dis;
}
double ClosestPairDistance(int start, int end)
{
int m = end-start+1, mid, i;
double t1, t2, dis=-1, t;
if(m <= 3) {
for(i=start; i < end; i++) {
t = Distance(pt[i] , pt[i+1]);
if(t < dis || dis == -1) dis = t;
}
t = Distance(pt[start] , pt[end]);
if(t < dis) dis=t;
return dis;
}
if(m%2 == 0) mid = start + m/2 - 1;
else mid = start + m/2;
if(m%2 == 0) {
t1 = ClosestPairDistance(start, mid);
t2 = ClosestPairDistance(mid+1, end);
}
else {
t1 = ClosestPairDistance(start, mid);
t2 = ClosestPairDistance(mid+1, end);
}
if(t1 < t2) dis = t1;
else dis = t2;
dis = Get(dis, mid, start, end);
return dis;
}
- 1. 球面上两点最短距离
// 计算圆心角lat表示纬度, -90 <= w <= 90, lng表示经度
// 返回两点所在大圆劣弧对应圆心角, 0 <= angle <= pi
double angle(double lng1, double lat1, double lng2, double lat2)
{
double dlng = fabs(lng1 - lng2) * pi / 180;
while(dlng >= pi+pi) dlng -= pi+pi;
if(dlng > pi) dlng = pi + pi - dlng;
lat1 *= pi / 180, lat2 *= pi / 180;
return acos( cos(lat1)*cos(lat2)*cos(dlng) + sin(lat1)*sin(lat2) );
}
// 计算距离, r为球半径
double line_dist(double r, double lng1, double lat1, double lng2, double lat2)
{
double dlng = fabs(lng1 - lng2) * pi / 180;
while(dlng >= pi+pi) dlng -= pi+pi;
if(dlng > pi) dlng = pi + pi - dlng;
lat1 *= pi / 180, lat2 *= pi / 180;
return r * sqrt( 2 - 2*( cos(lat1)*cos(lat2)*cos(dlng) + sin(lat1)*sin(lat2) ) );
}
// 计算球面距离, r为球半径
double sphere_dist(double r, double lng1, double lat1, double lng2, double lat2)
{
return r * angle(lng1, lat1, lng2, lat2);
}
- 2. 三点求圆心坐标
double GetRadiusBy3Points(double x1, double y1,
double x2, double y2,
double x3, double y3,
double &x, double &y)
{
// 由 ( x - x1 )^2 + ( y - y1 )^2 = ( x - x2 )^2 + ( y - y2 )^2 得
// 2*( x2 - x1 )*x + 2*( y2 - y1 )*y = x2^2 - x1^2 + y2^2 - y1^2
// 同理得
// 2*( x3 - x2 )*x + 2*( y3 - y2 )*y = x3^2 - x2^2 + y3^2 - y2^2
// 由行列式解方程得 x , y
double a11, a12, a21, a22, b1, b2;
double d, d1, d2 ;
a11 = 2 * ( x3 - x2 );
a12 = 2 * ( y3 - y2 );
a21 = 2 * ( x2 - x1 );
a22 = 2 * ( y2 - y1 );
b1 = x3*x3 - x2*x2 + y3*y3 - y2*y2;
b2 = x2*x2 - x1*x1 + y2*y2 - y1*y1;
d = a11*a22 - a12*a21;
d1 = b1*a22 - a12*b2;
d2 = a11*b2 - b1*a21;
// x , y 是圆心坐标
x = d1 / d;
y = d2 / d;
return (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y);
}
第七章专题讨论
- 1. 树状数组
/**** **** **** **** **** ****
* Function Name : 树状数组
* Description : HDOJ 1166 敌兵布阵
* 减少冗余统计, 是线段树的一种变化
**** **** **** **** **** ****/
#include<cstdio>
int data[50001], s[50001], T[50001];
inline int lowbit(int t)
{return t & (-t);}
inline int sum(int end)
{
int sum = 0;
while(end > 0) {
sum += T[end];
end -= lowbit(end);
}
return sum;
}
inline void plus(int pos, int num, int count)
{
while(pos <= count) {
T[pos] += num;
pos += lowbit(pos);
}
}
int main()
{
char buffer[10];
int i, j, t, n, a, b;
scanf("%d", &t);
for(i=1; i <= t ;i++) {
scanf("%d", &n);
T[0] = s[0] = data[0] = 0;
for(j=1; j <= n ;j++) {
scanf("%d", &data[j]);
s[j] = s[j - 1] + data[j];
T[j] = s[j] - s[j - lowbit(j)];
}
printf("Case %d:
", i);
while(scanf("%s", buffer) == 1 && buffer[0] != 'E') {
scanf("%d%d", &a, &b);
switch(buffer[0]) {
case 'Q':
printf("%d
", sum(b) - sum(a) + data[a]);
break;
case 'A':
plus(a, b, n);
data[a] += b; break;
case 'S':
plus(a, -b, n);
data[a] -= b; break;
}
}
}
}
- 2. 字典树
/**** **** **** **** **** ****
* Function Name : 字典树(多路查找树)
* Description : HDOJ 1075 What Are You Talking About
* 易于字符保存, 插入和查找, 时间复杂度都是线性
**** **** **** **** **** ****/
#include <cstdio>
#include <string>
using namespace std;
struct trie
{
trie * next[26];
int index;
};
trie *thead;
char dic[1000000][20];
inline trie * newnode()
{
int i;
trie *t;
t=(trie*)malloc(sizeof(trie));
memset(t,0,sizeof(trie));
return t;
}
void insert(trie * s,char x[],int pos)
{
int i;
trie *t;
for(i=0; x[i] ; i++) {
if( s->next[ x[i]-'a' ] ) s=s->next[ x[i]-'a' ];
else {
t=newnode();
s->next[ x[i]-'a' ]=t;
s=t;
}
}//for
s->index=pos;
}
void deltrie(trie * s)
{
int i;
for(i=0; i < 26 ;i++) {
if( s->next[i] )
deltrie(s->next[i]);
}
free(s);
s=NULL;
}
int find(trie *s, char x[])
{
int i;
if(x[0] == 0) return -1;
for(i=0; x[i] ; i++) {
if( s->next[ x[i]-'a' ] ) s=s->next[ x[i]-'a' ];
else break;
}
if(x[i]==0) return s->index;
else return -1;
}
int main()
{
int t,n,i,j,all;
char word[20],mars[20],ch;
thead=newnode();
while(scanf("%s",word)==1)
if(word[0]=='S') break;
i=1;
while(scanf("%s",dic[i])==1 && dic[i][0]!='E') {
scanf("%s",mars);
insert(thead,mars,i);
i++;
}
all=i;
while(scanf("%s",word)==1)
if(word[0]=='S') break;
getchar(); j=0;
while(scanf("%c",&ch)==1 && ch!='E') {
if(ch>='a' && ch<='z') {
mars[j]=ch; j++;
}
else {
mars[j]=0;
t=find( thead , mars );
j=0;
if(t>0) printf("%s",dic[t]);
else if(mars[0]!=0) printf("%s",mars);
printf("%c",ch);
}
}//while
deltrie(thead);
}
- 3. 后缀树
/**** **** **** **** **** ****
* Function Name : 后缀树
* Description : PKU 2774 Long Long Message
* 有效的支持字符串匹配和查询
**** **** **** **** **** ****/
#include<cstdio>
#include<string>
#define NUM 27
#define STARTCHAR 'a'
#define SPECIALCHAR '{'
#define ERROR -1
#define TYPE1 1
#define TYPE2 2
#define LEAF 1
#define NOTLEAF 2
struct SuffixTrie {
int Start, End;
SuffixTrie * Next[NUM];
SuffixTrie * Link;
SuffixTrie * Father;
int Flag;
int Length;
};
char str[100010], buf[100010];
SuffixTrie head;
SuffixTrie*P, *G, *U, *V, *q;
int W[3], len, len2;
void CreateNode(SuffixTrie * & Node) {
int i;
Node = (SuffixTrie * ) malloc(sizeof(SuffixTrie));
Node -> Start = Node -> End = Node -> Length = ERROR;
for (i = 0; i < NUM; i++) Node -> Next[i] = NULL;
Node -> Link = Node -> Father = NULL;
Node -> Flag = LEAF;
}
void Init(SuffixTrie & h, char s[]) {
int i;
h.Start = h.End = ERROR;
for (i = 0; i < NUM; i++) h.Next[i] = NULL;
h.Link = & h;
h.Father = NULL;
h.Flag = LEAF;
h.Length = 0;
len = strlen(s);
s[len] = SPECIALCHAR;
s[len + 1] = ' ';
len++;
}
int FindV(char s[]) {
int old;
SuffixTrie * t, * newt;
t = U -> Next[s[W[0]] - STARTCHAR];
old = 0;
while (W[2] > (t -> End) - (t -> Start) + 1 + old) {
old += (t -> End - t -> Start + 1);
t = t -> Next[s[W[0] + old] - STARTCHAR];
}
if (W[2] == (t -> End) - (t -> Start) + 1 + old) {
V = t;
P -> Link = V;
return TYPE1;
} else {
CreateNode(newt);
newt -> Start = t -> Start;
newt -> End = t -> Start + W[2] - old - 1;
newt -> Father = t -> Father;
newt ->
Length = newt -> Father -> Length + newt -> End - newt ->
Start + 1;
t -> Father -> Next[s[t -> Start] - STARTCHAR] = newt;
t -> Start = newt -> End + 1;
newt -> Next[s[t -> Start] - STARTCHAR] = t;
t -> Father = newt;
V = newt;
P -> Link = V;
return TYPE2;
}
}
int Insert(SuffixTrie * Node, int start, char s[]) {
int i, posbegin, posend;
SuffixTrie * t;
if (Node -> Next[s[start] - STARTCHAR] == NULL) {
CreateNode(Node -> Next[s[start] - STARTCHAR]);
Node -> Next[s[start] - STARTCHAR] -> Start = start;
Node -> Next[s[start] - STARTCHAR] -> End = len - 1;
Node -> Next[s[start] - STARTCHAR] -> Father = Node;
Node -> Next[s[start] - STARTCHAR] ->
Length = Node -> Length + len - start;
Node -> Flag = NOTLEAF;
P = Node;
return TYPE1;
} else {
posbegin = Node -> Next[s[start] - STARTCHAR] -> Start;
posend = Node -> Next[s[start] - STARTCHAR] -> End;
for (i = posbegin; i <= posend; i++) {
if (s[i] != s[start + i - posbegin]) break;
}
if (i == posend + 1) {
return Insert(Node -> Next[s[start] - STARTCHAR], start + i - posbegin, s);
} else {
CreateNode(t);
t -> Start = posbegin;
t -> End = i - 1;
t -> Flag = NOTLEAF;
Node -> Next[s[start] - STARTCHAR] -> Start = i;
t -> Next[s[i] - STARTCHAR] = Node -> Next[s[start] - STARTCHAR];
t -> Next[s[i] - STARTCHAR] -> Father = t;
Node -> Next[s[start] - STARTCHAR] = t;
t -> Father = Node;
t -> Length = Node -> Length + t -> End - t -> Start + 1;
Insert(t, start + i - posbegin, s);
G = Node;
P = t;
return TYPE2;
}
}
}
int Select(int start, char s[], int type) {
int result1, result2, result;
if (type == TYPE1) {
U = P -> Link;
result = Insert(U, start + U -> Length, s);
} else {
U = G -> Link;
if (G -> Link == G) {
W[0] = P -> Start + 1;
W[1] = P -> End;
W[2] = P -> End - P -> Start;
} else {
W[0] = P -> Start;
W[1] = P -> End;
W[2] = P -> End - P -> Start + 1;
}
if (W[2] == 0) {
V = G;
P -> Link = V;
result = Insert(V, start, s);
} else {
result1 = FindV(s);
result2 = Insert(V, start + V -> Length, s);
if (result1 == TYPE2) {
G = P -> Father;
result = result1;
} else result = result2;
}
}
return result;
}
void BuildSuffixTrie(SuffixTrie & h, char s[]) {
int i;
int type;
len = strlen(s);
CreateNode(h.Next[s[0] - STARTCHAR]);
h.Next[s[0] - STARTCHAR] -> Start = 0;
h.Next[s[0] - STARTCHAR] -> End = len - 1;
h.Next[s[0] - STARTCHAR] -> Father = & h;
h.Next[s[0] - STARTCHAR] -> Length = h.Length + h.Next[s[0] - STARTCHAR] -> End - h.Next[s[0] - STARTCHAR] -> Start + 1;
h.Flag = NOTLEAF;
type = TYPE1;
P = & h;
for (i = 1; i < len; i++) type = Select(i, s, type);
}
void DeleteSuffixTrie(SuffixTrie * & Node) {
int i;
for (i = 0; i < NUM; i++) {
if (Node -> Next[i] != NULL) {
DeleteSuffixTrie(Node -> Next[i]);
Node -> Next[i] = NULL;
}
}
free(Node);
}
int FindString(int start, char s[]) {
int result;
int i;
int temp;
SuffixTrie * x;
x = P -> Next[s[start] - STARTCHAR];
result = P -> Length;
if (x == NULL) {
P = P -> Link;
return result;
}
temp = 0;
for (i = start; i < len2; i++) {
if (x -> Start + i - start - temp > x -> End) {
temp = i - start;
P = x;
x = x -> Next[s[start + temp] - STARTCHAR];
if (x == NULL) break;
}
if (s[i] != str[x -> Start + i - start - temp]) break;
result++;
}
P = P -> Link;
return result;
}
int Search(SuffixTrie & h, char s[]) {
int result;
int i;
int temp;
len2 = strlen(s);
result = 0;
P = & head;
for (i = 0; i < len2; i++) {
temp = FindString(i + P -> Length, s);
if (result < temp) result = temp;
if (result >= len2 - i) break;
}
return result;
}
int main() {
int result;
while (scanf("%s", str) != EOF) {
Init(head, str);
BuildSuffixTrie(head, str);
scanf("%s", buf);
result = Search(head, buf);
printf("%d
", result);
}
}
- 4. 线段树
/**** **** **** **** **** ****
* Function Name : 线段树
* Description : HDOJ 1542 Atlantis
* 用于表示区间线段
**** **** **** **** **** ****/
#include<cstdio>
#include<algorithm>
using namespace std;
typedef struct ITREE_NODE {
ITREE_NODE * pLChild, * pRChild;
double left, right; // 左端点,右端点
double measure; // 测度
int count; // 覆盖计数器
int lines; // 独立线段数
int lbound, rbound; // 覆盖左、右顶点的线段数目
}*PITREE_NODE;
inline void safe_add(int & v, int value) {
v += value;
if (v < 0) v = 0;
}
void itree_splite(const double * pList, PITREE_NODE pParent, const int iLeft, const int iRight) {
if (iRight - iLeft <= 1) return;
int iMid = (iLeft + iRight) >> 1;
pParent -> pLChild = new ITREE_NODE;
pParent -> pRChild = new ITREE_NODE;
memset(pParent -> pLChild, 0, sizeof(ITREE_NODE));
memset(pParent -> pRChild, 0, sizeof(ITREE_NODE));
pParent -> pLChild -> left = pList[iLeft];
pParent -> pLChild -> right = pList[iMid];
pParent -> pRChild -> left = pList[iMid];
pParent -> pRChild -> right = pList[iRight];
itree_splite(pList, pParent -> pLChild, iLeft, iMid);
itree_splite(pList, pParent -> pRChild, iMid, iRight);
}
PITREE_NODE itree_generate(const double * pList, const int iListCount) {
PITREE_NODE pRoot = new ITREE_NODE;
memset(pRoot, 0, sizeof(ITREE_NODE));
pRoot -> left = pList[0];
pRoot -> right = pList[iListCount - 1];
itree_splite(pList, pRoot, 0, iListCount - 1);
return pRoot;
}
void itree_destroy(PITREE_NODE pParent) {
if (pParent == NULL) return;
if (pParent -> pLChild) itree_destroy(pParent -> pLChild);
if (pParent -> pRChild) itree_destroy(pParent -> pRChild);
delete pParent;
}
inline void itree_measure(PITREE_NODE pNode) {
if (pNode -> count > 0)
pNode -> measure = pNode -> right - pNode -> left;
else if (pNode -> pLChild && pNode -> pRChild)
pNode -> measure = pNode -> pLChild -> measure + pNode -> pRChild -> measure;
else
pNode -> measure = 0;
}
inline void itree_lines(PITREE_NODE pNode) {
if (pNode -> count > 0) {
pNode -> lines = 1;
} else if (pNode -> pLChild && pNode -> pRChild) {
if (pNode -> pLChild -> rbound && pNode -> pRChild -> lbound) {
pNode -> lines = pNode -> pLChild -> lines + pNode -> lines - 1;
} else {
pNode -> lines = pNode -> pLChild -> lines + pNode -> lines;
}
} else {
pNode -> lines = 0;
}
}
// 插入的时候value = 1, 删除的时候value = -1
void itree_update(PITREE_NODE pParent, const double left, const double right,
int value) {
if (pParent -> left == left && pParent -> right == right) {
safe_add(pParent -> count, value);
safe_add(pParent -> lbound, value);
safe_add(pParent -> rbound, value);
itree_measure(pParent);
itree_lines(pParent);
} else {
if (pParent -> pLChild -> right > left) {
if (pParent -> pLChild -> right >= right) {
itree_update(pParent -> pLChild, left, right, value);
} else {
itree_update(pParent -> pLChild, left,
pParent -> pLChild -> right, value);
itree_update(pParent -> pRChild, pParent -> pRChild -> left,
right, value);
}
} else {
itree_update(pParent -> pRChild, left, right, value);
}
itree_measure(pParent);
itree_lines(pParent);
if (left == pParent -> left) safe_add(pParent -> lbound, value);
if (right == pParent -> right) {
safe_add(pParent -> rbound, value);
}
}
}
void itree_insert(PITREE_NODE pParent, const double left, const double right) {itree_update(pParent, left, right, 1);
}
void itree_delete(PITREE_NODE pParent, const double left, const double right) {itree_update(pParent, left, right, -1);
}
struct EVENT {
double x, y1, y2;
int type;
};
bool cmp(const EVENT & a, const EVENT & b)
{ return a.x < b.x;
}
PITREE_NODE pRoot;
EVENT env[200];
double Y[200];
double tsize = 0.0;
int main() {
double x1, x2, y1, y2;
int i, n, n2, cas = 0;
while (scanf("%d", & n) == 1 && n) {
cas++;
n2 = n << 1;
for (i = 0; i < n2; i += 2) {
scanf("%lf%lf%lf%lf", & x1, & y1, & x2, & y2);
env[i].x = x1;
env[i].y1 = y1;
env[i].y2 = y2;
env[i].type = 1;
env[i + 1].x = x2;
env[i + 1].y1 = y1;
env[i + 1].y2 = y2;
env[i + 1].type = -1;
Y[i] = y1;
Y[i + 1] = y2;
}
sort(env, env + n2, cmp);
sort(Y, Y + n2);
pRoot = itree_generate(Y, n2);
for (i = 0; i < n2; ++i) {
if (i > 0) tsize += pRoot -> measure * (env[i].x - env[i - 1].x);
else tsize = 0.0;
itree_update(pRoot, env[i].y1, env[i].y2, env[i].type);
}
itree_destroy(pRoot);
printf("Test case #%d
Total explored area: %.2lf
", cas, tsize);
}
return 0;
}
- 5. 并查集
/**** **** **** **** **** ****
* Function Name : 并查集
* Description : 集合操作, 并, 除, 判断
**** **** **** **** **** ****/
const int Max=1000;
typedef int ElemType;
int Parent[Max],Rank[Max];
int Find(int x)
{
int temp = x, root, w;
//搜寻根节点
while(Parent[x]!=0) x=Parent[x];
root=x;
x=temp;
//压缩路径
while(Parent[x]!=0) {
w=Parent[x];
Parent[x]=root;
x=w;
}
return root;
}
int Union(int x,int y)
{
int u, v, root;
u=Find(x);
v=Find(y);
if(Rank[u] <= Rank[v]) {
root = Parent[u] = v;
if(Rank[u] == Rank[v]) Rank[v]++;
}
else root=Parent[v]=u;
return root;
}
- 6. 二叉堆
/**** **** **** **** **** ****
* Function Name : 二叉堆
* Description : 父结点的键值总是大於或等於任何一个子节点的键值
* 便於寻找父节点和子节点
**** **** **** **** **** ****/
const int Max=1000;
typedef int ElemType;
ElemType Heap[Max];
int Sift_Up(int i) //上移
{
ElemType temp;
bool flag;
flag = true;
if(i == 1) return 0;
do {
if(Heap[i] > Heap[i/2])
{temp=Heap[i]; Heap[i]=Heap[i/2]; Heap[i/2]=temp;}
else flag = false;
i /= 2;
}while(i>1 || flag);
return 1;
}
int Sift_Down(int i,int n) //下移
{
bool flag;
ElemType temp;
flag = false;
if(2*i > n) return 0;
do {
i*=2;
if(i+1 <= n && Heap[i+1] > Heap[i]) i++;
if(Heap[i/2] < Heap[i])
{temp=Heap[i]; Heap[i]=Heap[i/2]; Heap[i/2]=temp;}
else flag = false;
}while(2*i<=n || flag);
return 1;
}
int Insert(int &n,ElemType x) //插入元素
{
Heap[++n] = x;
if( Sift_Up(n) ) return n;
}
int Delete(int &n,int i) //输出元素
{
ElemType x,y;
x = Heap[i]; y = Heap[n];
n--;
if(i == n+1) return x;
Heap[i] = y;
if(y >= x) Sift_Up(i);
else Sift_Down(i,n);
return x;
}
int Delete_Max(int &n) //输出最大值
{
ElemType x;
x = Heap[1];
Delete(n,1);
return x;
}
int Make_Heap(int n) //转换为大顶堆
{
int i;
for(i=n/2; i >= 1 ;i--) Sift_Down(i,n);
return n;
}
int HeapSort(int n) //非降序排序
{
int i;
ElemType temp;
Make_Heap(n);
for(i=n; i >= 2 ;i--) {
temp=Heap[i]; Heap[i]=Heap[1]; Heap[1]=temp;
Sift_Down(1,i-1);
}
return 1;
}
- 7. 逆序数(归并排序)
/**** **** **** **** **** ****
* Function Name : 逆序数(归并排序)
* Description : N*Log(N)
**** **** **** **** **** ****/
//逆序数值存放在anti中
int p[MAX], t[MAX], anti = 0;
void merge(int first, int last)
{
int mid = (first+last)/2;
int i1 = 0, i2 = first, i3 = mid+1;
while(i2 <= mid && i3 <= last) {
if(p[i2] > p[i3]) {
t[i1++] = p[i3++];
anti += mid-i2+1;
}
else t[i1++]=p[i2++];
}
while(i2 <= mid) t[i1++] = p[i2++];
while(i3 <= last) t[i1++] = p[i3++];
i1 = first; i2 = 0;
while(i2 < last-first+1) p[i1++] = t[i2++];
}
void merge_sort(int first, int last)
{
int mid;
if(first<last) {
mid = (first+last)/2;
merge_sort(first, mid);
merge_sort(mid+1, last);
merge(first, last);
}
}
- 8. 树状DP
/**** **** **** **** **** ****
* Function Name : 树状DP
* Description : HDOJ 1561 The more, The Better
**** **** **** **** **** ****/
#include<cstdio>
#include<memory>
#include<queue>
using namespace std;
#define Max 210
int n,m,a[Max][Max];
struct inf
{
int l,r,p;
int v;
}tree[Max];
int tp,now;
queue<int> SQ;
char v[Max];
int main()
{
int i,j;
int root,pt,tv;
while(scanf("%d%d",&n,&m)) {
if(n==0 && m==0 ) break;
memset(tree,0,sizeof(tree));
memset(a,0,sizeof(a));
memset(v,3,sizeof(v));
while(!SQ.empty()) SQ.pop();
for(i=1; i <= n ;i++) {
scanf("%d%d", &root, &tree[i].v);
if(tree[root].l == 0) {
tree[root].l = i;
v[root]--;
tree[i].p = root;
}
else {
pt = tree[root].l;
while(tree[pt].r != 0) pt = tree[pt].r;
tree[pt].r = i;
v[pt] -= 2;
tree[i].p = pt;
}
}
for(i=1;i<=n;i++)
if(v[i]==3) SQ.push(i);
while(!SQ.empty()) {
now = SQ.front();
SQ.pop();
a[now][1] = tree[now].v;
for(i=1; i <= m ;i++)
a[now][i] = a[now][i] < a[ tree[now].r ][i] ? a[ tree[now].r ][i] : a[now][i];
for(i=2; i <= m ;i++)
for(j=1; j <= i ;j++) {
tv = a[ tree[now].l ][j-1] + tree[now].v + a[ tree[now].r ][i-j];
a[now][i] = a[now][i] < tv ? tv : a[now][i];
}
if(tree[ tree[now].p ].l == now) v[ tree[now].p ]++;
else v[ tree[now].p ]
+= 2;
if(v[ tree[now].p ] == 3) SQ.push(tree[now].p);
}
printf("%d
",a[ tree[0].l ][m]);
}
}
- 9. 欧拉路
/**** **** **** **** **** ****
* Function Name : 欧拉路
* Description : ZJU 2730 Necklace
* 欧拉路的构造方法:
* 若图连同且度为奇数的节点不超过2个,则该图可以构造出欧拉路
* 先选一个度为奇数的节点(若没有就任选一个度为偶数的节点)
* 再以该节点为起点,用dfs遍历所有的弧(每条弧只遍历一次),遇到死胡同就回溯
* 在每次回溯时将所在弧按顺序记录下来,这组弧的排列就组成了一条欧拉路
**** **** **** **** **** ****/
#include<stdio.h>
#define MAXN 50
void find_path_euler(int n, int mat[][MAXN], int now, int& step, int* path)
{
int i;
for(i=n-1; i >= 0 ;i--)
while(mat[now][i]) {
mat[now][i]--, mat[i][now]--;
find_path_u(n, mat, i, step, path);
}
path[ step++ ]=now;
}
int main()
{
int n;
int a[MAXN][MAXN];
int i, j, cnt, mmin;
int b[10000],c[10000];
while(scanf("%d",&n)!=EOF) {
for(i=0; i<n ;i++)
for(j=0; j<n ;j++)
if(j == i) a[i][j] = 0;
else a[i][j] = a[j][i] = 1;
cnt = 0;
mmin = 2000000000;
for(i=0; i<n ;i++) {
find_path_u(n, a, i, cnt, b);
if(cnt < mmin) {
mmin = cnt;
for(j=0; j<mmin ;j++)
c[j] = b[j];
break;
}
}
printf("%d
", mmin-1);
for(i=0; i<mmin-2 ;i++)
printf("%d ",c[i]);
printf("%d",c[i]);
printf("
");
}
}
- 10. 八数码
/**** **** **** **** **** ****
* Function Name : 八数码Eight(Special Judge)
* Description : 搜索 + 状态hash
* PKU(1077) HDOJ(1043) ZOJ(1217)
* BFS 广搜 PKU(312ms) HDOJ(TLE) ZOJ(TLE)
* BFS2 双向广搜 PKU(31ms) HDOJ(1325ms) ZOJ(TLE)
**** **** **** **** **** ****/
#include<cstdio>
#include<string>
#include<memory>
#include<queue>
using namespace std;
char input[100];
int state[10], s_num, e10[10], fac_n[10];
char hash_T[400000], step[10000], hash_T2[400000];
struct inf
{
int pos;
char mode;
};
queue<int> SQ;
queue<inf> SQ2;
int num_pos(int num,int x,int y)
{
int temp=(x-1)*3+y;
if(temp == num%10) return 9;
if(temp > num%10) return (num / e10[9-temp+1]) %10;
else return (num / e10[9-temp] )%10;
}
int state_pos(int num,int x,int y)
{
int temp=(x-1)*3+y;
if(temp == state[9]) return 9;
if(temp > state[9]) return state[temp-1];
else return state[temp];
}
inline int move(int num,char op)
{
int t0,t1,t2;
switch(op)
{
case 'r':
if(num%10%3 == 0) return 0;
return num+1;
case 'l':
if((num-1)%10%3 ==0) return 0;
return num-1;
case 'u':
if(num%10 -3 <= 0) return 0;
t0 = 9-num%10 + 1;
t1 = num / e10[t0];
t2 = t1%1000;
t1= t1- t2 + (t2 % 100) * 10 + t2 / 100;
t1*= e10[t0];
return (t1 + ( (num % e10[t0]) - 3));
case 'd':
if(num%10 +3 > 9) return 0;
t0 = 9-num%10 + 1 -3;
t1 = num / e10[t0];
t2 = t1%1000;
t1= t1- t2 + (t2 % 10) * 100 + t2 / 10;
t1*= e10[t0];
return (t1 + ( (num % e10[t0]) + 3));
}
}
bool be_solved()
{
int i,j,anti=0;
for(i=1;i<=8;i++)
for(j=1;j<i;j++)
if( state[i] < state[j] )
anti++;
if(anti%2) return false;
else return true;
}
inline int hash(int num)
{
int dig[10],i=9,j,sum,anti;
if(num==0) return -1;
while(num) dig[i]=num%10 , num/=10 , i-- ;
sum=(9-dig[9])*fac_n[8];
for(i=1;i<9;i++) {
for(anti=0,j=1;j<i;j++)
if(dig[i] < dig[j])
anti++;
sum += anti*fac_n[i-1];
}
return sum;
}
void BFS()
{
int k,to_num,to_hash,i;
memset(hash_T,0,sizeof(hash_T));
while(!SQ.empty()) SQ.pop();
SQ.push(123456789);
hash_T[ hash(123456789) ]='e';
while(!SQ.empty())
{
k=SQ.front();
SQ.pop();
to_num=move(k,'r'); to_hash=hash(to_num);
if(to_hash>=0 && hash_T[ to_hash ]==0)
hash_T[ to_hash ]='r' , SQ.push(to_num);
to_num=move(k,'l'); to_hash=hash(to_num);
if(to_hash>=0 && hash_T[ to_hash ]==0)
hash_T[ to_hash ]='l' , SQ.push(to_num);
to_num=move(k,'u'); to_hash=hash(to_num);
if(to_hash>=0 && hash_T[ to_hash ]==0)
hash_T[ to_hash ]='u' , SQ.push(to_num);
to_num=move(k,'d'); to_hash=hash(to_num);
if(to_hash>=0 && hash_T[ to_hash ]==0)
hash_T[ to_hash ]='d' , SQ.push(to_num);
}
}
void BFS2()
{
int to_num,to_hash,i;
char *phash,*phash2;
char op;
inf k,t;
memset(hash_T,0,sizeof(hash_T));
memset(hash_T2,0,sizeof(hash_T2));
while(!SQ2.empty()) SQ2.pop();
k.pos=s_num; k.mode=1;
SQ2.push(k);
k.pos=123456789; k.mode=2;
SQ2.push(k);
hash_T[ hash(s_num) ]='s';
hash_T2[ hash(123456789) ]='e';
while(!SQ2.empty()) {
k=SQ2.front();
SQ2.pop();
to_hash=hash(k.pos);
if(k.mode==1)
if(hash_T2[ to_hash ]!=0) break;
else phash=hash_T,phash2=hash_T2;
if(k.mode==2)
if(hash_T[ to_hash ]!=0) break;
else phash=hash_T2,phash2=hash_T;
t=k;
t.pos=move(k.pos,'r'); to_hash=hash(t.pos);
if(to_hash>=0 && phash[ to_hash ]==0)
phash[ to_hash ]='r' , SQ2.push(t);
t.pos=move(k.pos,'l'); to_hash=hash(t.pos);
if(to_hash>=0 && phash[ to_hash ]==0)
phash[ to_hash ]='l' , SQ2.push(t);
t.pos=move(k.pos,'u'); to_hash=hash(t.pos);
if(to_hash>=0 && phash[ to_hash ]==0)
phash[ to_hash ]='u' , SQ2.push(t);
t.pos=move(k.pos,'d'); to_hash=hash(t.pos);
if(to_hash>=0 && phash[ to_hash ]==0)
phash[ to_hash ]='d' , SQ2.push(t);
}
i=0;
to_hash = hash(k.pos);
to_num = k.pos;
while( hash_T[ to_hash ] != 's' ) {
switch( step[i++]=hash_T[ to_hash ] ) {
case 'r': op='l';break;
case 'l': op='r';break;
case 'u': op='d';break;
case 'd': op='u';break;
}
to_num=move(to_num,op);
to_hash=hash(to_num);
}
while(i>0) printf("%c",step[--i]);
to_hash=hash(k.pos);
to_num=k.pos;
while( hash_T2[ to_hash ]!='e' ) {
switch( hash_T2[ to_hash ] ) {
case 'r': op='l';break;
case 'l': op='r';break;
case 'u': op='d';break;
case 'd': op='u';break;
}
printf("%c",op);
to_num=move(to_num, op );
to_hash=hash(to_num);
}
}
int main()
{
int i,j;
for(e10[0]=1,i=1;i<=9;i++)
e10[i] =e10[i-1]*10;
for(fac_n[0]=0,fac_n[1]=1,i=2;i<=9;i++)
fac_n[i] =fac_n[i-1]*i;
while( gets(input) ) {
for(i=strlen(input)-1,j=8;i>=0;i--) {
if(input[i]!=' ') {
if(input[i]=='x')
state[9]=j+1;
else state[j--]=input[i]-'0';
}
}
for(s_num=0,i=9,j=1;i>0;i--,j*=10)
s_num += state[i]*j;
if( !be_solved() )
printf("unsolvable
");
else {
BFS2();
printf("
");
}
}
}
- 11. 高斯消元法
/**** **** **** **** **** ****
* Function Name : 高斯消元法
* Description : 求解线性方程组
*
* void exchange_col(int p1,int p2,int n)
* 交换p1行和p2行的所有数据
*
* bool gauss(int n)
* 求解系数矩阵为n的线性方程组,方程组无解返回false,否则true
*
* x1 = x0 - f(x0)/f'(x0) 牛顿迭代法
**** **** **** **** **** ****/
const int num = 100;
double matrix[num][num + 1]; //系数矩阵,从0开始
double ans[num]; //结果数组
void exchange_col(int p1,int p2,int n) //交换p1行和p2行的所有数据
{
double t;
int i;
for(i = 0 ; i <= n ; i++)
t = matrix[p1][i],matrix[p1][i] = matrix[p2][i],matrix[p2][i] = t;
}
bool gauss(int n) //求解系数矩阵为n的线性方程组
{
int i,j,k;
int p;
double r;
for(i = 0 ; i < n - 1 ; i++) {
p = i;
for(j = i + 1 ; j < n ; j++) { //寻找i列最大值位置
if(matrix[j][i] > matrix[p][i])
p = j;
}
if(matrix[p][i] == 0) return false;
if(p != i) exchange_col(i,p,n);
for(j = i + 1 ; j < n ; j++) { //剩余列进行消元
r = matrix[j][i] / matrix[i][i];
for(k = i ; k <= n ; k++)
matrix[j][k] -= r * matrix[i][k];
}
}
for(i = n - 1 ; i >= 0 ; i--) { //获得结果
ans[i] = matrix[i][n];
for(j = n - 1 ; j > i ; j--)
ans[i] -= matrix[i][j] * ans[j];
ans[i] /= matrix[i][i];
}
return true;
}
- 12. 字符串匹配(KMP算法)
/**** **** **** **** **** ****
* Function Name : 字符串匹配(KMP算法)
* Description : O(N+M)
**** **** **** **** **** ****/
void get_nextval(const string & s, int * p)
{
int i = 0,j = -1;
p[0] = -1;
while(i < s.size()) {
if(j == -1 || s[i] == s[j]) {
++i,++j;
if(s[i] != s[j]) p[i] = j;
else
p[i] = p[j];
}
else j = p[j];
}
}
int Index_KMP(const string & s, const string & s1, int pos)
{
int i = pos - 1,j = 0;
int * next = new int[s1.size()];
get_nextval(s1,next);
while(i <= s.size() && j <= s1.size()) {
if(j == -1 || s[i] == s1[j]) ++i,++j;
else j = next[j];
}
if(j > s1.size()) return i - s1.size();
else return -1;
}
- 13. 全排列,全组合
/**** **** **** **** **** ****
* Function Name : 全排列,全组合
**** **** **** **** **** ****/
void createper(int n) //全排列
{
int total,i,j,k,t,*a=new int[n],top;
total=1;
for(i=1;i<=n;i++) {
a[i]=i;
total*=i;
}
for(i=1;i<n;i++) printf("%d ",a[i]);
printf("%d
",a[n]);
for(i=1;i<total;i++) {
j=n;
while(a[j]<a[j-1]) j--;
k=n;
while(a[j-1]>a[k]) k--;
t=a[j-1];
a[j-1]=a[k];
a[k]=t;
top=(j+n-1)/2;
for(k=j;k<=top;k++) {
t=a[k];
a[k]=a[n-k+j];
a[n-k+j]=t;
}
for(j=1;j<n;j++) printf("%d ",a[j]);
printf("%d
",a[n]);
}
}
void createfab(int m,int n) //全组合
{
int i,j,lcount,*a=new int[n+2];
for(i=1;i<=n;i++) a[i]=i;
a[n+1]=m+1;
for(j=1;j<n;j++) printf("%d ",a[j]);
printf("%d
",a[n]);
lcount=1;
while(a[1]<m-n+1) {
for(i=n;i>0;i--) {
if(a[i]<a[i+1]-1) {
a[i]++;
for(j=i;j<n;j++) a[j+1]=a[j]+1;
for(j=1;j<n;j++) printf("%d ",a[j]);
printf("%d
",a[n]);
lcount++;
break;
}
}
}
}