数据类型的取值范围
char |
-128 ~ 127 (1 Byte,大约3位) |
short |
-32768 ~ 32767 (2 Bytes,大约五位) |
unsigned short |
0 ~ 65536 (2 Bytes,大约五位) |
int |
-2147483648 ~ 2147483647 (4 Bytes,大约十位) |
unsigned int |
0 ~ 4294967295 (4 Bytes,大约十位) |
long
扫描二维码关注公众号,回复:
102069 查看本文章
|
== int |
long long |
-9223372036854775808 ~ 9223372036854775807 (8 Bytes,大约十九位) |
unsigned long long |
0 ~ 18446744073709551615(大约二十位) |
__int64 |
== long long |
unsigned __int64 |
== unsigned long long |
double |
1.7 * 10^308 (8 Bytes) |
using namespace std;
const int maxn = 110;
博弈论
Bash
#define _MAX 10000
int a[_MAX];
int b[_MAX];
intbash(
intN,
intK)
{
if
(N % (K +
1) ==
0)
{
return
2
;
}
return
1
;
}
int main()
{
int
T;
scanf(
"%d", &T);
for
(
inti =
0; i < T; i++)
{
scanf(
"%d%d", a + i, b + i);
}
for
(
inti =
0; i < T; i++)
{
if
(bash(a[i], b[i]) ==
1)
{
printf
(
"A\n");
}
else
{
printf
(
"B\n");
}
}
return
0
;
}
Nim
intmain(
intargc,
constchar
* argv[])
{
int
N, stone, tag =
0;
scanf
(
"%d", &N);
while
(N--)
{
scanf
(
"%d", &stone);
tag ^= stone;
}
//tag为0则为后手赢,否则为先手赢
printf
(
"%c\n", tag ==
0?
'B':
'A');
return
0
;
}
SG函数
SG打表
constint
MAX_DIG =
64;
// SG打表
// f[]:可以取走的石子个数
// sg[]:0~n的SG函数值
// hash[]:mex{}
int f[MAX_DIG];
int sg[MAX_DIG];
int hash[MAX_DIG];
voidgetSG(
intn)
{
memset
(sg,
0,
sizeof(sg));
for
(
inti =
1; i <= n; i++)
{
memset
(hash,
0,
sizeof(hash));
for
(
intj =
1; f[j] <= i; j++)
{
hash[sg[i - f[j]]] =
1;
}
for
(
intj =
0; j <= n; j++)
// 求mes{}中未出现的最小的非负整数
{
if
(hash[j] ==
0)
{
sg[i] = j;
break
;
}
}
}
}
SG DFS
constint
MAX_DIG =
64;
// DFS
// 注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
// n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[MAX_DIG];
intsg[MAX_DIG *
100];
int n;
intSG_dfs(
intx)
{
if
(sg[x] != -
1)
{
return
sg[x];
}
bool
vis[MAX_DIG];
memset
(vis,
0,
sizeof(vis));
for
(
inti =
0; i < n; i++)
{
if
(x >= s[i])
{
SG_dfs(x - s[i]);
vis[sg[x - s[i]]] =
1;
}
}
int
e;
for
(
inti =
0; ; i++)
{
if
(!vis[i])
{
e = i;
break
;
}
}
return
sg[x] = e;
}
Wythoff有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品
int main()
{
int
t, a, b, m, k;
scanf
(
"%d", &t);
while
(t--)
{
scanf
(
"%d%d", &a, &b);
if
(a > b)
{
a ^= b;
b ^= a;
a ^= b;
}
m = b - a;
k = (
int)(m * (
1+
sqrt(
5)) /
2.0);
//m = ? * a
//k = m / ?
//?:黄金分割数
//如果a == k,则为后手赢,否则先手赢(奇异局)
printf
(
"%s\n", a == k ?
"B":
"A");
}
return
0
;
}
经典
1.埃拉托斯特尼筛法/*|埃式筛法||快速筛选素数|*/
int prime[maxn];
bool is_prime[maxn];
int sieve(int n){
int p = 0;
for(int i = 0;i <= n; ++i)
is_prime[i]= true;
is_prime[0] =is_prime[1] = false;
for (int i = 2;i <= n; ++i){ // 注意数组大小是n
if(is_prime[i]){
prime[p++] = i;
for(intj = i + i; j <= n; j += i) // 轻剪枝,j必定是i的倍数
is_prime[j] = false;
}
}
return p; // 返回素数个数
}
2.快速幂
typedef long long LL; // 视数据大小的情况而定
LL powerMod(LL x, LL n, LL m)
{
LL res = 1;
while (n >0){
if (n & 1) // 判断是否为奇数,若是则true
res =(res * x) % m;
x = (x * x)% m;
n >>=1; // 相当于n /= 2;
}
return res;
}
3.大数模拟
大数加法
string add1(string s1, string s2)
{
if (s1 =="" && s2 == "") return "0";
if (s1 =="") return s2;
if (s2 =="") return s1;
string maxx =s1, minn = s2;
if (s1.length()< s2.length()){
maxx = s2;
minn = s1;
}
int a =maxx.length() - 1, b = minn.length() - 1;
for (int i = b;i >= 0; --i){
maxx[a--]+= minn[i] - '0'; // a一直在减 , 额外还要减个'0'
}
for (int i =maxx.length()-1; i > 0;--i){
if (maxx[i]> '9'){
maxx[i]-= 10;//注意这个是减10
maxx[i- 1]++;
}
}
if (maxx[0]> '9'){
maxx[0] -=10;
maxx = '1'+ maxx;
}
return maxx;
}
大数阶乘
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int num[maxn], len;
/*
在mult函数中,形参部分:len每次调用函数都会发生改变,n表示每次要乘以的数,最终返回的是结果的长度
tip: 阶乘都是先求之前的(n-1)!来求n!
初始化Init函数很重要,不要落下
*/
void Init() {
len = 1;
num[0] = 1;
}
int mult(int num[], int len, int n) {
LL tmp = 0;
for(LL i = 0; i< len; ++i) {
tmp = tmp+ num[i] * n; //从最低位开始,等号左边的tmp表示当前位,右边的tmp表示进位(之前进的位)
num[i] =tmp % 10; // 保存在对应的数组位置,即去掉进位后的一位数
tmp = tmp/ 10; // 取整用于再次循环,与n和下一个位置的乘积相加
}
while(tmp){ // 之后的进位处理
num[len++]= tmp % 10;
tmp = tmp/ 10;
}
return len;
}
int main() {
Init();
int n;
n = 1977; // 求的阶乘数
for(int i = 2;i <= n; ++i) {
len =mult(num, len, i);
}
for(int i = len- 1; i >= 0; --i)
printf("%d",num[i]); // 从最高位依次输出,数据比较多采用printf输出
printf("\n");
return 0;
}
4.GCD最大公约数 |辗转相除法||欧几里得算法|
int gcd(int big, int small)
{
if (small >big) swap(big, small);
int temp;
while (small !=0){ // 辗转相除法
if (small> big) swap(big, small);
temp = big% small;
big =small;
small =temp;
}
return(big);
}
5.LCM最小公倍数 |辗转相除法||欧几里得算法|
int gcd(int big, int small)
{
if (small >big) swap(big, small);
int temp;
while (small !=0){ // 辗转相除法
if (small> big) swap(big, small);
temp = big% small;
big =small;
small =temp;
}
return(big);
}
6.全排列|求1到n的全排列, 有条件|
void Pern(int list[], int k, int n) { // k表示前k个数不动仅移动后面n-k位数
if (k == n - 1){
for (int i= 0; i < n; i++) {
printf("%d", list[i]);
}
printf("\n");
}else {
for (int i= k; i < n; i++) { // 输出的是满足移动条件所有全排列
swap(list[k], list[i]);
Pern(list, k + 1, n);
swap(list[k], list[i]);
}
}
}
7.二分搜索 |二分搜索||要求:先排序|
// left为最开始元素, right是末尾元素的下一个数,x是要找的数
int bsearch(int *A, int left, int right, int x){
int m;
while (left< right){
m = left +(right - left) / 2;
if (A[m]>= x) right = m; else left = m + 1;
// 如果要替换为 upper_bound, 改为:if (A[m]<= v) x = m+1; else y = m;
}
return left;
}
/*
最后left == right
如果没有找到135577找6,返回7
如果找有多少的x,可以用lower_bound查找一遍,upper_bound查找一遍,下标相减
C++自带的lower_bound(a,a+n,x)返回数组中最后一个x的下一个数的地址
upper_bound(a,a+n,x)返回数组中第一个x的地址
如果a+n内没有找到x或x的下一个地址,返回a+n的地址
lower_bound(a,a+n,x)-upper_bound(a,a+n,x)返回数组中x的个数
*/
8.并查集 |合并节点操作|
int father[maxn]; // 储存i的father父节点
void makeSet() {
for (int i = 0;i < maxn; i++)
father[i] =i;
}
int findRoot(int x) { // 迭代找根节点
int root = x;// 根节点
while (root !=father[root]) { // 寻找根节点
root =father[root];
}
while (x !=root) {
int tmp =father[x];
father[x] =root; // 根节点赋值
x =tmp;
}
returnroot;
}
void Union(int x, int y) { // 将x所在的集合和y所在的集合整合起来形成一个集合。
int a, b;
a =findRoot(x);
b =findRoot(y);
father[a] =b; // y连在x的根节点上 或father[b] = a为x连在y的根节点上;
}
/*
在findRoot(x)中:
路径压缩 迭代 最优版
关键在于在路径上的每个节点都可以直接连接到根上
*/
最小生成树
Kruskal
9.克鲁斯卡尔算法|Kruskal算法|
|适用于 稀疏图 求最小生成树|
/*
第一步:点、边、加入vector,把所有边按从小到大排序
第二步:并查集部分 + 下面的code
*/
void Kruskal() {
ans = 0;
for (int i = 0;i<len; i++) {
if(Find(edge[i].a) != Find(edge[i].b)) {
Union(edge[i].a, edge[i].b);
ans +=edge[i].len;
}
}
}
Prim
10.普里姆算法 |Prim算法||适用于稠密图 求最小生成树||堆优化版,时间复杂度:O(elgn)|
struct node {
int v,len;
node(int v = 0,int len = 0) :v(v), len(len) {}
bool operator< (const node &a)const { // 加入队列的元素自动按距离从小到大排序
returnlen> a.len;
}
};
vector<node> G[maxn];
int vis[maxn];
int dis[maxn];
void init() {
for (int i = 0;i<maxn; i++) {
G[i].clear();
dis[i] = INF;
vis[i] =false;
}
}
int Prim(int s) {
priority_queue<node>Q; // 定义优先队列
int ans =0;
Q.push(node(s,0)); // 起点加入队列
while(!Q.empty()) {
node now =Q.top(); Q.pop(); // 取出距离最小的点
int v = now.v;
if (vis[v])continue; // 同一个节点,可能会推入2次或2次以上队列,这样第一个被标记后,剩下的需要直接跳过。
vis[v] =true; // 标记一下
ans +=now.len;
for (int i= 0; i<G[v].size(); i++) { // 开始更新
int v2= G[v][i].v;
int len = G[v][i].len;
if(!vis[v2] && dis[v2] > len) {
dis[v2] = len;
Q.push(node(v2, dis[v2])); // 更新的点加入队列并排序
}
}
}
return ans;
}
单源最短路
Dijkstra
11.迪杰斯特拉算 |适用于边权为正的有向图或者无向图||求从单个源点出发,到所有节点的最短路|
struct node {
int v,len;
node(int v = 0,int len = 0) :v(v), len(len) {}
bool operator< (const node &a)const { // 距离从小到大排序
return len> a.len;
}
};
vector<node>G[maxn];
bool vis[maxn];
int dis[maxn];
void init() {
for (int i = 0;i<maxn; i++) {
G[i].clear();
vis[i] =false;
dis[i] =INF;
}
}
int dijkstra(int s, int e) {
priority_queue<node>Q;
Q.push(node(s,0)); // 加入队列并排序
dis[s] =0;
while(!Q.empty()) {
node now =Q.top(); // 取出当前最小的
Q.pop();
int v =now.v;
if (vis[v])continue; // 如果标记过了, 直接continue
vis[v] =true;
for (int i = 0; i<G[v].size(); i++){ // 更新
int v2= G[v][i].v;
int len= G[v][i].len;
if(!vis[v2] && dis[v2] > dis[v] + len) {
dis[v2] = dis[v] + len;
Q.push(node(v2, dis[v2]));
}
}
}
returndis[e];
}
SPFA
12.最短路径快速算法(Shortest Path FasterAlgorithm)|SPFA算法||队列优化| |可处理负环|
vector<node> G[maxn];
bool inqueue[maxn];
int dist[maxn];
void Init()
{
for(int i = 0 ;i < maxn ; ++i){
G[i].clear();
dist[i] =INF;
}
}
int SPFA(int s,int e)
{
intv1,v2,weight;
queue<int> Q;
memset(inqueue,false,sizeof(inqueue)); // 标记是否在队列中
memset(cnt,0,sizeof(cnt)); // 加入队列的次数
dist[s] = 0;
Q.push(s); // 起点加入队列
inqueue[s] =true; // 标记
while(!Q.empty()){
v1 =Q.front();
Q.pop();
inqueue[v1]= false; // 取消标记
for(int i =0 ; i < G[v1].size() ; ++i){ // 搜索v1的链表
v2 =G[v1][i].vex;
weight= G[v1][i].weight;
if(dist[v2] > dist[v1] + weight){ // 松弛操作
dist[v2] = dist[v1] + weight;
if(inqueue[v2] == false){ // 再次加入队列
inqueue[v2] = true;
//cnt[v2]++; // 判负环
//if(cnt[v2] > n) return -1;
Q.push(v2);
} }}
}
returndist[e];
}
/*
不断的将s的邻接点加入队列,取出不断的进行松弛操作,直到队列为空
如果一个结点被加入队列超过n-1次,那么显然图中有负环
*/
Floyd-Warshall
13.弗洛伊德算法
|Floyd算法| |任意点对最短路算法| |求图中任意两点的最短距离的算法|
for (int i = 0; i < n; i++) { // 初始化为0
for (int j = 0;j < n; j++)
scanf("%lf", &dis[i][j]);
}
for (int k = 0; k < n; k++) {
for (int i = 0;i < n; i++) {
for (int j = 0; j < n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
14.染色法|交叉染色法判断二分图|
int bipartite(int s) {
int u, v;
queue<int>Q;
color[s] =1;
Q.push(s);
while (!Q.empty()){
u =Q.front();
Q.pop();
for (int i= 0; i < G[u].size(); i++) {
v =G[u][i];
if(color[v] == 0) {
color[v] = -color[u];
Q.push(v);
}
else if (color[v] == color[u])
return 0;
}
}
return 1;
}
15..匈牙利算法 |求解最大匹配问题||递归实现|
vector<int>G[maxn];
bool inpath[maxn]; // 标记
int match[maxn]; // 记录匹配对象
void init()
{
memset(match,-1, sizeof(match));
for (int i = 0;i < maxn; ++i) {
G[i].clear();
}
}
bool findpath(int k) {
for (int i = 0;i < G[k].size(); ++i) {
int v =G[k][i];
if(!inpath[v]) {
inpath[v] = true;
if(match[v] == -1 || findpath(match[v])) { // 递归
match[v] = k; // 即匹配对象是“k妹子”的
return true;
}
}
}
returnfalse;
}
void hungary() {
int cnt =0;
for (int i = 1;i <= m; i++) { // m为需要匹配的“妹子”数
memset(inpath, false, sizeof(inpath)); // 每次都要初始化
if(findpath(i)) cnt++;
}
cout <<cnt << endl;
}
/*|求解最大匹配问题| |dfs实现|*/
int v1, v2;
bool Map[501][501];
bool visit[501];
int link[501];
int result;
bool dfs(int x) {
for (int y = 1;y <= v2; ++y) {
if(Map[x][y] && !visit[y]) {
visit[y] = true;
if(link[y] == 0 || dfs(link[y])) {
link[y] = x;
return true;
} }}
returnfalse;
}
void Search() {
for (int x = 1;x <= v1; x++) {
memset(visit,false,sizeof(visit));
if(dfs(x))
result++;
}
}
16.17.18背包问题
// 01背包:
void bag01(int cost,int weight) {
for(i = v; i>= cost; --i)
dp[i] =max(dp[i], dp[i-cost]+weight);
}
// 完全背包:
void complete(int cost, int weight) {
for(i = cost ;i <= v; ++i)
dp[i] =max(dp[i], dp[i - cost] + weight);
}
// 多重背包:
void multiply(int cost, int weight, int amount) {
if(cost *amount >= v)
complete(cost, weight);
else{
k = 1;
while (k< amount){
bag01(k* cost, k * weight);
amount-= k;
k +=k;
}
bag01(cost* amount, weight * amount);
}
}
// other
int dp[1000000];
int c[55], m[110];
int sum;
void CompletePack(int c) {
for (int v = c;v <= sum / 2; ++v){
dp[v] =max(dp[v], dp[v - c] + c);
}
}
void ZeroOnePack(int c) {
for (int v =sum / 2; v >= c; --v) {
dp[v] =max(dp[v], dp[v - c] + c);
}
}
void multiplePack(int c, int m) {
if (m * c >sum / 2)
CompletePack(c);
else{
int k = 1;
while (k < m){
ZeroOnePack(k * c);
m -= k;
k<<= 1;
}
if (m !=0){
ZeroOnePack(m * c);
}
}
}
状态转移dp[i] = max{ 1.dp[j] + 1 }; j<i; a[j]<a[i];
d[i]是以i结尾的最长上升子序列
与i之前的 每个a[j]<a[i]的 j的位置的最长上升子序列+1后的值比较
*/
void solve(){ // 参考挑战程序设计入门经典;
for(int i = 0;i < n; ++i){
dp[i] =1;
for(int j =0; j < i; ++j){
if(a[j]< a[i]){
dp[i] = max(dp[i], dp[j] + 1);
} } }
}
/*
优化方法:
dp[i]表示长度为i+1的上升子序列的最末尾元素
找到第一个比dp末尾大的来代替
*/
void solve(){
for (int i= 0; i < n; ++i){
dp[i] =INF;
}
for (int i= 0; i < n; ++i) {
*lower_bound(dp, dp + n, a[i]) = a[i]; // 返回一个指针
}
printf("%d\n", *lower_bound(dp, dp + n, INF) - dp;
}
/*
函数lower_bound()返回一个 iterator 它指向在[first,last)标记的有序序列中可以插入value,而不会破坏容器顺序的第一个位置,而这个位置标记了一个不小于value的值。
*/
20.最长公共子序列 |求最长公共子序列||递推形式|
void solve() {
for (int i = 0;i < n; ++i) {
for (int j= 0; j < m; ++j) {
if(s1[i] == s2[j]) {
dp[i + 1][j + 1] = dp[i][j] + 1;
}else{
dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
} } }
}
22.求多边形面积
node G[maxn];
int n;
double Cross(node a, node b) { // 叉积计算
return a.x*b.y- a.y*b.x;
}
int main()
{
while(scanf("%d", &n) != EOF && n) {
for (int i= 0; i < n; i++)
scanf("%lf %lf", &G[i].x, &G[i].y);
double sum= 0;
G[n].x =G[0].x;
G[n].y =G[0].y;
for (int i= 0; i < n; i++) {
sum+= Cross(G[i], G[i + 1]);
}
// 或者
//for(int i = 0; i < n; i++) {
//sum += fun(G[i], G[(i + 1)% n]);
//}
sum = sum /2.0;
printf("%.1f\n", sum);
}
system("pause");
return 0;
}
23..判断线段相交
/*
|16/11/06ztx|
*/
node P[35][105];
double Cross_Prouct(node A,node B,node C) { // 计算BA叉乘CA
return(B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);
}
bool Intersect(node A,node B,node C,node D) { // 通过叉乘判断线段是否相交;
if(min(A.x,B.x)<=max(C.x,D.x)&& // 快速排斥实验;
min(C.x,D.x)<=max(A.x,B.x)&&
min(A.y,B.y)<=max(C.y,D.y)&&
min(C.y,D.y)<=max(A.y,B.y)&&
Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&& // 跨立实验;
Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0) // 叉乘异号表示在两侧;
returntrue;
else returnfalse;
}
24.求三角形外心
Point circumcenter(const Point &a, const Point&b, const Point &c) { //返回三角形的外心
Point ret;
double a1 = b.x- a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1) / 2;
double a2 = c.x- a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2) / 2;
double d =a1*b2 - a2*b1;
ret.x = a.x +(c1*b2 - c2*b1) / d;
ret.y = a.y +(a1*c2 - a2*c1) / d;
returnret;