板子整理

板子整理

目錄

  • 排序(快排及其原理、sort、歸併、以及STL中的compare寫法)
  • 遞歸(排列問題、dfs、斐波拉契)
  • 二分(主要爲例題)
  • dp問題彙總(揹包、子序列、樹形dp例題等等)
  • 計算幾何(凸包、叉積)
  • 圖算法(最小生成樹、最大流、最短路徑、二分圖)
  • 字符串匹配(有限自動機、KMP)
  • FFT(各種應用)
  • 常見小技巧(關閉輸入輸出流、快速冪)

1、排序

快排原理html

int Partition(int i,int j){
    while(i<=j){
        while(a[i]<mid) i++;//mid是本身給出的分割線,小的在左,大的在右
        while(a[j]>mid) j--;
        if(i<=j){
            int tmp;
            tmp=a[i];
            a[i]=a[j];
            a[j]=tmp;
            i++;
            j--;
        }
    }//結束後i即爲分割點的下標
    return i;
}

sortnode

#include<algorithm>
sort(begin,end,cmp);//sort函數的寫法,cmp可省略
sort(begin,end,less<int>());//升序
sort(begin,end,greater<int>());//降序
sort(str.begin(),str.end());//字符類型排序
sort(str.rbegin(),str.rend());//反向迭代器完成逆序
sort(x,x+4,cmp);//結構體排序
bool cmp(node x,node y)
{
    if(x.a==y.a)
        return x.b>y.b;
    return x.a>y.a;
}

歸併排序ios

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std; 
long long count=0;
void merge(int x[ ],int tmp[ ],int left,int leftend,int rightend)
{     
    int i=left, j=leftend+1, q=left;
    while(i<=leftend && j<=rightend)
    {   
        if(x[i]<=x[j]){
            //count[4]++;
            tmp[q++]=x[i++];
        } 
        else{
            count+=leftend-i+1;//這裏爲答案
            tmp[q++]=x[j++];
        }           
    }
    while(i<=leftend)
        tmp[q++]=x[i++];
    while(j<=rightend)
        tmp[q++]=x[j++];
    for(i=left; i<=rightend; i++)
        x[i]=tmp[i];
} 
void mSort(int k[],int tmp[],int left,int right){
    int center;
    if (left<right){
        center=(left+right)/2;
        mSort(k,tmp,left,center);
        mSort(k,tmp,center+1,right);
        merge(k,tmp,left,center,right);
    }
}
void mergeSort(int k[],int n){
    int *tmp;
    tmp=(int *)malloc(sizeof(int)*n);
    if(tmp!=NULL){
        mSort(k,tmp,0,n-1);
        free(tmp);
    }
}
int main(){
    int n,a[100001];
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    mergeSort(a,n);
    cout<<count;
}

優先隊列及其cmp寫法c++

priority_queue<int,vector<int>,less<int> > q;// 從大到小
priority_queue<int,vector<int>,greater<int> > q;// 從小到大
priority_queue<pair<int,int> > q;//第一個爲準,相等比第二個
priority_queue<node> q;//結構體,自定義排序
//(1) 重載bool operator<,寫在結構體外面
struct node{
      int x, y;
      node(int x=0, int y=0):x(x),y(y){}
};
bool operator<(node a, node b){//可寫成const node &a或者const node a
      if(a.x > b.x) return 1;
      else if(a.x == b.x)
           if(a.y >= b.y)   return 1;
      return 0;
}
//(2) 重載bool operator<,寫在結構體裏面
struct node{
      int x, y;
      node(int x=0, int y=0):x(x),y(y){}
      bool operator<(const node &b) const{
           if(x > b.x) return 1;
           else if(x == b.x)
                 if(y >= b.y) return 1;
           return 0;
      }
};
//(3) 友元函數
struct node{
      int x, y;
      node(int x=0, int y=0):x(x),y(y){}
      friend bool operator<(const node&a, const node &b){
           if(a.x > b.x) return 1;
           else if(a.x == b.x)
                 if(a.y >= b.y)   return 1;
           return 0;
      }
};
//(4) 重載(),自定義cmp
priority_queue<int, vector<int>, cmp> q;
struct node{
      int x, y;
      node(intx=0, int y=0):x(x),y(y){}
};
struct cmp{
      bool operator()(const node &a, const node &b){
           if(a.x> b.x) return 1;
           else if(a.x == b.x)
                 if(a.y>= b.y)   return 1;
           return 0;
      }
};

2、遞歸

排列問題算法

//全排列
#include<stdio.h>
int n,a[50],b[50];
void f(int depth){
    if(depth==0){
        for(int i=0;i<n;i++){
            printf("%d ",a[i]);
        }
        printf("\n");
    }
    else{
        for(int i=1;i<=n;i++){
            if(b[i]==0){
                a[n-depth]=i;
                b[i]=1;
                f(depth-1);
                b[i]=0;
            }
        }
    }
}
int main(){
    scanf("%d",&n);
    f(n);
}
//給定n,m,輸出從 1∼n中選擇 m個數的全部排列。 要求按照字典序輸出。
void f(int depth){
    int i;
    if (depth==n-m){
        for (i=0;i<m;i++){
            printf("%d ",a[i]);
        }
        printf("\n");
        return;
    }
    for (i=1;i<=n;i++){
        if (b[i]==0){
            a[n-depth]=i;
            b[i]=1;
            f(depth-1);
            b[i]=0;
        }
    }
}
//從1~n中選取任意多(大於0)個數字,輸出全部可能的選擇方案
void f(int depth,int cur)
{
    for (int i=cur+1;i<=n;i++)//從該數的下一個數開始遞歸
    {
        if (b[i]==0)
        {
            a[depth]=i;
            b[i]=1;
            for(int j=1;j<=depth;j++){
                printf("%d",a[j]);
            }
            printf("\n");
            f(depth+1,i);
            b[i]=0;         
        }
    }
}
int main(){
    scanf("%d",&n);
    f(1,0);
}
//類循環排列
#include <stdio.h> 
#define MAX_N  10 
int n, m;//至關於n重循環,每重循環長度爲m 
int rcd[MAX_N];     //記錄每一個位置填的數 
void loop_permutation(int l){  
    int i;  
    if (l == n) {              //至關於進入了n重循環的內層   
        for (i=0; i<n; i++){    
            printf("%d", rcd[i]);    
            if (i < n-1) printf(" ");   
        }   
        printf("\n"); 
        return ;  
    }  
    for (i=0; i<m; i++){        //每重循環長度爲m   
        rcd[l] = i;            //在l位置放i   
        loop_permutation(l+1); //填下一個位置  
    } 
} 
int main(void){  
    while (scanf("%d%d", &n, &m) != EOF) 
        loop_permutation(0);  
    return 0; 
}

食物鏈數組

如今給你n個物種和m條能量流動關係,求其中的食物鏈條數。網絡

物種的名稱爲從1到n的編號。m條能量流動關係形如a b 表示能量從物種a 流向物種b。注意單獨的一種孤立生物不算一條食物鏈。求食物鏈總數less

//備忘錄式遞歸
#include<stdio.h>
#include<stdlib.h>
#include<vector>
using namespace std;
vector<int> g[100005];
int in[100005]={},out[100005]={},vis[100005]={};
int dfs(int temp){
    int sum=0;
    if(in[temp]&&!out[temp]){
        vis[temp]=1;
        return 1;
    }
    if(vis[temp]) return vis[temp];
    else{
        for(int i=0;i<g[temp].size();i++){
            sum+=dfs(g[temp][i]);
        }
    }
    vis[temp]=sum;
    return sum;
}
int main(){
    int n,m,a,b,ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        g[a].push_back(b);
        out[a]++;
        in[b]++;
    }
    for(int i=1;i<=n;i++){
        if(!in[i]&&out[i]){
            ans+=dfs(i);
        }
    }
    printf("%d",ans); 
}

斐波拉契函數

//兔子問題
f(n)=f(n-1)+f(n-2);//普通狀況
f(n)=f(n-1)+f(n-2)-f(n-10);//十天後衰老
f(n)=f(n-1)+f(n-2)-f(n-10);
f(n)=f(n)-f(n-10);//十天後死去,這是在循環完後再減
f(n)=f(n-1)+f(n-2)-f(n-10);
f(n)=f(n)-f(n-15);//十天後衰老,十五天後死去,這是在循環完後再減
//青蛙上臺階
f(n)=f(n-1)+f(n-2);//能夠跳一階或者兩階
f(n)=2*f(n-1);//能夠跳1~n階
f(n)=f(n-1)+f(n-2)+f(n-4)+f(n-5);//跳了一次三階後,以後須要隔一次才能再跳三階的

漢諾塔oop

void hanoi(int n,char from,char tmp,char to){
    if (n>0) {
        hanoi(n - 1, from, to, tmp);
        move(from,to);//輸出函數
        hanoi(n - 1, tmp, from, to);
    }
}
void move(char from,char to){
    cout << "get game from board " << from << endl;
    cout << "playing" << endl;//這一行能夠省略
    cout << "put game to board " << to << endl;
}

3、二分

二分廣泛寫法

int r=1e6,l=0,mid,ans;
while(l<=r){
    mid=(l+r)/2;
    if(check(mid)) ans=mid,r=mid-1;//check()即爲檢驗函數
    else l=mid+1;
}
//看到是求最大值最小、最小值最大這一類的,就確定爲二分
//時間複雜度爲nlogn時,也應該往二分靠攏

二分查找大於等於v的第一個值

//保證l<=r,返回值l合理
int bs(int a[],int l,int r,int v){
    int m;
    while(l<r){
        m=(l+r)>>1;
        if(a[m]<v) l=m+1;
        else r=m;
    }
    return l;
}

放一道例題

一個無向圖,N個點編號1~NM條邊,每條邊有一個權值c。對於一個點集A,這個點集的權值S定義爲SA=max cij,其中i∈A∧j∈A∧i≠j。如今將N個點分割爲兩個點集A、B,請問max(SA,SB)max(SA,SB) 的最小值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define max 300005
using namespace std;
typedef long long ll;
struct node {
    int a,b,c;
} ver[max];
struct Edge {
    int v;
    int c;
    int next;
} e[max];
int head[max],e_num=0;
int n,m,S,T;
ll mid;
int color[max],vis[max];
void add(int u,int v,int c) {
    e[e_num].v=v;
    e[e_num].c=c;
    e[e_num].next=head[u];
    head[u]=e_num;
    e_num++;
}
void insert(int u,int v,int c) {
    add(u,v,c);
    add(v,u,c);
}
bool dfs(int u, int c)
{
    vis[u]=1;
    color[u]=c;
    for(int i=head[u];~i;i=e[i].next)
    {
        int j=e[i].v;
        if(!color[j])
        {
            if(!dfs(j, 3-c)) return false;
        }
        else if(color[j]==c) return false;
    }
    return true;
}
bool check() {
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(color,0,sizeof(color));
    for(int i=1; i<=m; i++) {
        if(ver[i].c>mid) 
            insert(ver[i].a,ver[i].b,ver[i].c);//若是大則連邊
    }
    for(int i=1; i<=n; i++){
        if(!vis[i]){
        if(!dfs(i,1)) return false;
        }
    }
    return true;
}
int main() {
    int a,b,c;
    ll ans;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++) {
        scanf("%d%d%d",&ver[i].a,&ver[i].b,&ver[i].c);
    }
    ll l=0,r=3*1e10;
    while(l<=r){
        mid=((l+r)>>1);
        if(check()) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%lld",ans);
}

4、DP問題

揹包問題

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
struct node{
    int value;
    int price;
    int num;
};
struct node a[505];
int b[30005]={},val;
int max(int n,int m){
    if(n>=m) return n;
    return m;
}
void ZeroOnePack(int *b,int price,int value){
    int v;
    for(v=val;v>=price;v--){
        b[v]=max(b[v],b[v-price]+value);
    }
}
void CompletePack(int *b,int price,int value){
    int v;
    for(v=price;v<=val;v++){
        b[v]=max(b[v],b[v-price]+value);
    }
}
void MultiplePack(int *b,int price,int value,int num){
    if (price*num>=val){
        CompletePack(b,price,value);
        return;
    }
    int k=1;
    while(k<num){
        ZeroOnePack(b,k*price,k*value);
        num=num-k; 
        k=2*k; 
    }
    ZeroOnePack(b,price*num,value*num);
}
int main(){
    int n,i,j,v;
    while(~scanf("%d%d",&n,&val)){
        for(i=1;i<=n;i++){
            scanf("%d%d%d",&a[i].price,&a[i].value,&a[i].num);
        }
        for(i=0;i<=val;i++){
            b[i]=0;
        }
        for(i=1;i<=n;i++){
            MultiplePack(b,a[i].price,a[i].value,a[i].num);
        }
        printf("%d\n",b[val]);
    }
}

股票問題

#include<iostream>
#include<cstdio>
using namespace std;
int max(int n,int m){
    if(n>=m) return n;
    return m;
}
int main(){
    int n,k,a[100005]={};
    long long buy[1005],sell[1005];
    while(~scanf("%d%d",&n,&k)){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=k;i++){
            buy[i]=-1000000001;
            sell[i]=0;
        }
        for(int i=1;i<=n;i++){
            buy[1]=max(buy[1],-a[i]);
            sell[1]=max(sell[1],buy[1]+a[i]);
            for(int j=2;j<=k;j++){
                buy[j]=max(buy[j],sell[j-1]-a[i]);
                sell[j]=max(sell[j],buy[j]+a[i]);
            }
        }
        printf("%lld\n",sell[k]);
    }
}

樹形dp例題

二叉樹

最長鏈爲這棵二叉樹中一條最長的簡單路徑,即不通過重複結點的一條路徑。能夠容易證實,二叉樹中最長鏈的起始、結束結點均爲葉子結點。現給出一棵N(N<=100000)個結點二叉樹,問這棵二叉樹中最長鏈的長度爲多少,保證了1號結點爲二叉樹的根。

#include<stdio.h>
#include<stdlib.h>
int max(int a,int b){
    if(a>b) return a;
    return b;
}
int l[100005],r[100005];
int g[100005],f[100005];
void DFS(int depth){
    if(depth!=0){
        DFS(l[depth]);
        DFS(r[depth]);
        g[depth]=max(g[l[depth]],g[r[depth]])+1;
    }
    else{
        g[depth]=0;
    }
}
int main()
{
    int tmax=-1;
    int n,i;
    scanf("%d",&n);
    for (i=1;i<=n;i++){
        scanf("%d%d",&l[i],&r[i]);
    }
    DFS(1);
    for(int i=1;i<=n;i++){
        f[i]=1+g[l[i]]+g[r[i]];
        if(f[i]>tmax){
            tmax=f[i];
        }
    }
    printf("%d",tmax-1);
 }

子序列問題

最長有序子序列LOS

從一個數字序列中找出他的最長上升(降低,非降低,非上升)序列

上升爲例 輸入14235,輸出長度4,及序列1235

#include<stdio.h>
#define max ...
int main(){
    int dp[max],a[max],n;
    int ans[max],cnt=0;// 序列記錄數組
    // 輸入n及a[]
    dp[0]=1;
    for(int i=1;i<n;i++){
        for(int j=0;j<i;j++){
            if(a[i]>a[j]){ // 上升子序列
                if(dp[i]<dp[j]+1){// 這裏須要加一的緣由是在dp[i]更新後要保持一致,否則會出錯
                    dp[i]=dp[j]+1;
                    pre[i]=j;// 表明子序列前一個數的下標
                }
            }
        }
    }
}

最長公共序列 LCS

從兩個數字序列中找出他們的最長公共子序列

輸入14235 12435,輸出長度4,序列1235

dp[i][j]表示s1序列前i個元素和s2序列前j個元素的最長公共子序列長度

#define max ...
int main(){
    int dp[max][max],s1[max],s2[max];
    int n1,n2;
    //輸入s1,s2
    dp[0][0]=0;
    for(int i=1;i<=n1;i++){
        for(int j=1;j<=n2;j++){
            if(s1[i-1]==s2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
                pre[i][j]=0;// 同時減一便可
            }
            else{
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                if(dp[i][j]==dp[i-1][j]) pre[i][j]=1;// n1-1
                else pre[i][j]=2;// n2-1
            }
        }
    }
}
// 打印子序列(逆序的)
void prin(int n1,int n2){
    if(n1==0||n2==0) return;
    if(!pre[n1][n2]){
        printf("%d",s1[n1-1]);
        prin(n1-1,n2-1);
    }
    else if(pre[n1][n2]==1) prin(n1-1,n2);
    else prin(n1,n2-1);
}

最長公共遞增子序列 LCIS

從兩個數字序列中找出他的最長公共遞增子序列,公共的同時須要遞增

輸入1254367 125836,輸出長度4,序列1236

dp[i][j]爲s1的前i元素和s2前j元素中,以s2[j]結尾的LCIS

#define max ...
int main(){
    int dp[max][max],s1[max],s2[max];
    int n1,n2;
    //輸入s1,s2
    dp[0][0]=0;
    for(int i=1;i<=n1;i++){
        int max_len=0;
        for(int j=1;j<=n2;j++){
            if(s1[i-1]>s2[j-1]&&dp[i-1][j]>max_len) max_len=dp[i-1][j];
            if(s1[i-1]==s1[j-1]) dp[i][j]=max_len+1;
            else dp[i][j]=dp[i-1][j];
        }
    }
}
//序列問題暫時沒有想到較好的方法

最長迴文子序列 LPS

從一個數字序列中找出他的最長迴文子序列(左右元素相等,有對稱軸)

輸入:1254332,輸出:長度4,2332

dp[j][i]爲s的0~i個元素和s的n~j個元素的最長LPS

//最簡單的解決方案:逆轉後對兩個數組取LCS,多了逆轉和新數列,時間空間佔用較大
//...
//正常作法以下
#define max ...
int main(){
    int dp[max][max],s[max];
    int n;
    //輸入s1,s2
    for(int i=0;i<n;i++){
        dp[i][i]=1;
        for(int j=i-1;j>=0;j--){
            if(s[i]==s[j]){
                dp[j][i]=dp[j+1][i-1]+2;
                //pre[j+1][i+1]=0;
            }
            else{
                if(dp[j+1][i]>dp[j][i-1]){
                    dp[j][i]=dp[j+1][i];
                    //pre[j+1][i+1]=1;
                }
                if(dp[j+1][i]<dp[j][i-1]){
                    dp[j][i]=dp[j][i-1];
                    //pre[j+1][i+1]=2;
                }
            }
        }
    }
}

最長等差子序列

現有一數字序列,從中取出一些數字元素,就能夠組成一個等差數列,咱們想知道這個等差數列最多能有多少個元素,原序列每一個元素最多隻能取一次

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
short int dp[10001][10001];
int a[10001]= {};
int main() {
    int n,ans,i,j,k;
    while(~scanf("%d",&n)) {
        for(i=0; i<n; i++) {
            scanf("%d",&a[i]);
        }
        sort(a,a+n);
        for(i=0; i<n; i++) {
            for(j=i+1; j<n; j++) {
                dp[i][j]=2;
            }
        }
        ans=2;
        for(j=n-2; j>0; j--) {
            i=j-1,k=j+1;
            while(i>=0&&k<n) {
                if(a[i]+a[k]>2*a[j]) {
                    i--;
                } else if(a[i]+a[k]<2*a[j]) {
                    k++;
                } else {
                    dp[i][j]=dp[j][k]+1;
                    if(dp[i][j]>ans) ans=dp[i][j];
                    i--,k++;
                }
            }
        }
        printf("%d\n",ans);
    }
}

5、計算幾何

對於兩根向量a⃗ ×b⃗
$$
\vec{a}\times\vec{b}=x_ay_b-x_by_a<0那麼\vec{a}在\vec{b}的逆時針方向
$$

(通俗的理解,假設朝上的話,那麼a在b的左邊),反之亦然。

算面積和周長(凸包)

#include<cstdio>
#include<algorithm>
#include<cmath>
#define rint register int
using namespace std;

struct node {
    double x,y;
} a[100005];
int n,p,st[100005],top;
double ans,miny=2e9,minx=2e9;

int cmp(node b,node c) { //極角排序
    if (fabs((b.y-miny)*(c.x-minx)-(c.y-miny)*(b.x-minx))<=1e-8) return fabs(minx-b.x)<fabs(minx-c.x);
    return (b.y-miny)*(c.x-minx)<(c.y-miny)*(b.x-minx);
}

int check(int b,int c,int d) { //叉積判斷
    return ((a[b].x*a[c].y)+(a[c].x*a[d].y)+(a[d].x*a[b].y)-(a[b].x*a[d].y)-(a[c].x*a[b].y)-(a[d].x*a[c].y))>0;
}

double dist(double x1,double y1,double x2,double y2) { //計算兩點間的歐幾里得距離
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

int main() {
    rint i;
    scanf("%d",&n);
    for (i=1; i<=n; ++i) {
        scanf("%lf%lf",&a[i].x,&a[i].y);
        if (a[i].y<miny) { //尋找最下方的點
            miny=a[i].y;
            minx=a[i].x;
        }
    }
    sort(a+1,a+1+n,cmp); //極角排序
    st[1]=1;
    st[2]=2;
    top=2; //將兩個點加入棧中
    for (i=3; i<=n; ++i) { //掃描
        while (!check(st[top-1],st[top],i)) top--;
        st[++top]=i;
    }
    for (i=2; i<=top; ++i) //計算答案
        ans+=dist(a[st[i-1]].x,a[st[i-1]].y,a[st[i]].x,a[st[i]].y);
    ans+=dist(a[st[top]].x,a[st[top]].y,a[1].x,a[st[1]].y);
    double area=0;
    for(i=1;i<top;i++){
        area+=(a[st[i]].x*a[st[i+1]].y-a[st[i+1]].x*a[st[i]].y);
    } 
    area+=(a[st[top]].x*a[st[1]].y-a[st[1]].x*a[st[top]].y);
    area/=2;
    printf("%.2lf %.2lf",ans,area);
    return 0;
}
/*板子求凸包
CALL:nr=graham(point pnt[],int n,point res[])pnt[]爲輸入的點積
O(NlogN)
res[]即爲求得的凸包得點積
*/
struct point {
    double x, y;
};
bool mult(point sp, point ep, point op) {
    return (sp.x - op.x) * (ep.y - op.y)   >= (ep.x - op.x) * (sp.y - op.y);
}
bool operator < (const point &l, const point &r) {
    return l.y < r.y || (l.y == r.y && l.x < r.x);
}
int graham(point pnt[], int n, point res[]) {
    int i, len, k = 0, top = 1;
    sort(pnt, pnt + n);
    if (n == 0) return 0;
    res[0] = pnt[0];
    if (n == 1) return 1;
    res[1] = pnt[1];
    if (n == 2) return 2;
    res[2] = pnt[2];
    for (i = 2; i < n; i++) {
        while (top && mult(pnt[i], res[top], res[top-1]))
            top--;
        res[++top] = pnt[i];
    }
    len = top;
    res[++top] = pnt[n - 2];
    for (i = n - 3; i >= 0; i--) {
        while (top!=len && mult(pnt[i], res[top], res[top-1])) top--;
        res[++top] = pnt[i];
    }
    return top;       // 返回凸包中點的個數
}

凸包相交

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
class Point {
public:
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    Point operator+(Point a) {
        return Point(a.x + x, a.y + y);
    }
    Point operator-(Point a) {
        return Point(x - a.x, y - a.y);
    }
    bool operator<(const Point &a) const {
        if (x == a.x)
            return y < a.y;
        return x < a.x;
    }
    bool operator==(const Point &a) const {
        if (fabs(x - a.x) < eps && fabs(y - a.y) < eps)
            return 1;
        return 0;
    }
    double length() {
        return sqrt(x * x + y * y);
    }
};
typedef Point Vector;
double cross(Vector a, Vector b) {
    return a.x * b.y - a.y * b.x;
}//叉積
double dot(Vector a, Vector b) {
    return a.x * b.x + a.y * b.y;
}//點積
bool isclock(Point p0, Point p1, Point p2) {
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if (cross(a, b) < -eps)
        return true;
    return false;
}//判斷平行也就是夾角很小很小

double getDistance(Point a, Point b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

typedef vector<Point> Polygon;
Polygon Andrew(Polygon s){
    Polygon u,l;
    if(s.size()<3) return s;
    sort(s.begin(), s.end());//根據x座標排序 
    u.push_back(s[0]);
    u.push_back(s[1]);
    l.push_back(s[s.size()-1]);
    l.push_back(s[s.size()-2]);
    for(int i=2;i<s.size();++i){
        for(int n=u.size();n>=2&&!isclock(u[n-2],u[n-1],s[i]);--n){
            u.pop_back();
        }
        u.push_back(s[i]);
    }
    for(int i = s.size() - 3 ; i >= 0 ; --i) {
        for(int n = l.size() ; n >=2 && !isclock(l[n-2],l[n-1],s[i]); --n) {
            l.pop_back();
        }
        l.push_back(s[i]);
    }
    for(int i = 1 ; i < u.size() - 1 ; i++) l.push_back(u[i]);
    return l;
}

int dcmp(double x)  {
    if (fabs(x) <= eps)
        return 0;
    return x > 0 ? 1 : -1;
}

// 判斷點在線段上
bool OnSegment(Point p, Point a1, Point a2) {
    return dcmp(cross(a1 - p, a2 - p)) == 0 && dcmp(dot(a1 - p, a2 - p)) < 0;
}

// 判斷線段相交
bool Intersection(Point a1, Point a2, Point b1, Point b2) {
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1),
            c3 = cross(b2 - b1, a1 - b1), c4 = cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

// 判斷點在凸包內
int isPointInPolygon(Point p, vector<Point> s) {
    int wn = 0, cc = s.size();
    for (int i = 0; i < cc; i++) {
        Point p1 = s[i];
        Point p2 = s[(i + 1) % cc];
        if (p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1;
        int k = dcmp(cross(p2 - p1, p - p1));
        int d1 = dcmp(p1.y - p.y);
        int d2 = dcmp(p2.y - p.y);
        if (k > 0 && d1 <= 0 && d2 > 0) wn++;
        if (k < 0 && d2 <= 0 && d1 > 0) wn--;
    }
    if (wn != 0) return 1;
    return 0;
}

void solve(Polygon s1, Polygon s2) {
    int c1 = s1.size(), c2 = s2.size();
    for(int i = 0; i < c1; ++i) {
        if(isPointInPolygon(s1[i], s2)) {//點是否包含
            printf("NO\n");
            return;
        }
    }
    for(int i = 0; i < c2; ++i) {
        if(isPointInPolygon(s2[i], s1)) {//同上
            printf("NO\n");
            return;
        }
    }
    for (int i = 0; i < c1; i++) {
        for (int j = 0; j < c2; j++) {
            if (Intersection(s1[i], s1[(i + 1) % c1], s2[j], s2[(j + 1) % c2])) {//線段相交判斷
                printf("NO\n");
                return;
            }
        }
    }
    printf("YES\n");
}
int main() {
    int n,m;
    while (cin>>n>>m){
        if(n==0&&m==0) break;
        Polygon s1,s2;
        for (int i=0;i<n;++i){
            double x1, x2;
            scanf("%lf%lf",&x1,&x2);
            s1.push_back(Point(x1, x2));
        }
        for (int i=0;i<m;++i){
            double x1, x2;
            scanf("%lf%lf",&x1,&x2);
            s2.push_back(Point(x1,x2));
        }
        if(s1.size()) s1=Andrew(s1);
        if(s2.size()) s2=Andrew(s2);
        solve(s1,s2);
    }
    return 0;
}

線段相交

#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
class Point{
    public: 
        double x,y;
        Point(double x=0, double y=0):x(x),y(y) {}
        Point operator + (Point p){
            return Point(x+p.x,y+p.y);//重定義加法,點的加法即座標相加,也多是點和向量相加 
        }
        Point operator - (Point p){
            return Point(x-p.x,y-p.y);//重定義減法,點的減法即座標相減 
        }
        Point operator * (double a){
            return Point(a*x,a*y);//重定義乘法,點乘常數即以座標乘常數 
        }
}; 
typedef Point Vector;//由於向量Vector也能用X,Y表示
int flag; 
struct Segment{ //Segment 線段
    Point p1,p2;
};
double cross(Vector a, Vector b) {//向量的外積 
    return a.x*b.y - a.y*b.x;
}
double crossx(Point p1,Point p2,Point q1,Point q2){//也是外積不過是具體的點之間的 
    return (p1.x-p2.x)*(q1.y-q2.y)-(p1.y-p2.y)*(q1.x-q2.x);//p1p2 x q1q2 
}
bool issame(Point P1,Point P2,Point Q1,Point Q2) {
    if((P1.x==Q1.x&&P1.y==Q1.y)&&(!(P2.x==Q2.x&&P2.y==Q2.y))) { //P1=Q1
        printf("%lf %lf\n",P1.x,P1.y);
        return true;
    } else if((!(P1.x==Q1.x&&P1.y==Q1.y))&&(P2.x==Q2.x&&P2.y==Q2.y)) { //P2=Q2
        printf("%lf %lf\n",P2.x,P2.y);
        return true;
    } else if((P2.x==Q1.x&&P2.y==Q1.y)&&(!(P1.x==Q2.x&&P1.y==Q2.y))) { //P2=Q1
        printf("%lf %lf\n",P2.x,P2.y);
        return true;
    } else if((!(P2.x==Q1.x&&P2.y==Q1.y))&&(P1.x==Q2.x&&P1.y==Q2.y)) { //P1=Q2
        printf("%lf %lf\n",P1.x,P1.y);
        return true;
    } else return false;
}
bool isch(Point P1,Point P2,Point Q1,Point Q2){ 
    if(//存在兩個端點均與另外一線段重合
        ((P2.y-Q1.y)*(Q1.x-P1.x)==(Q1.y-P1.y)*(P2.x-Q1.x)&&(((P1.x<=Q1.x)&&(P2.x>=Q1.x))||((P1.x>=Q1.x)&&(P2.x<=Q1.x)))&&(((P1.y<=Q1.y)&&(P2.y>=Q1.y))||((P1.y>=Q1.y)&&(P2.y<=Q1.y)))&&
        (P2.y-Q2.y)*(Q2.x-P1.x)==(Q2.y-P1.y)*(P2.x-Q2.x)&&(((P1.x<=Q2.x)&&(P2.x>=Q2.x))||((P1.x>=Q2.x)&&(P2.x<=Q2.x)))&&(((P1.y<=Q2.y)&&(P2.y>=Q2.y))||((P1.y>=Q2.y)&&(P2.y<=Q2.y))))||
        ((Q2.y-P1.y)*(P1.x-Q1.x)==(P1.y-Q1.y)*(Q2.x-P1.x)&&(((Q1.x<=P1.x)&&(Q2.x>=P1.x))||((Q1.x>=P1.x)&&(Q2.x<=P1.x)))&&(((Q1.y<=P1.y)&&(Q2.y>=P1.y))||((Q1.y>=P1.y)&&(Q2.y<=P1.y)))&&
        (Q2.y-P2.y)*(P2.x-Q1.x)==(P2.y-Q1.y)*(Q2.x-P2.x)&&(((Q1.x<=P2.x)&&(Q2.x>=P2.x))||((Q1.x>=P2.x)&&(Q2.x<=P2.x)))&&(((Q1.y<=P2.y)&&(Q2.y>=P2.y))||((Q1.y>=P2.y)&&(Q2.y<=P2.y))))
    )
        return true;
    return false;
}
bool judge(Point p1,Point p2,Point q1,Point q2){//判斷是否相交 
    if(crossx(p1,q1,p1,p2)*crossx(p1,q2,p1,p2)<0&&crossx(q1,p1,q1,q2)*crossx(q1,p2,q1,q2)<0) 
        return true;//正常相交 
    else if((crossx(p1,q1,p1,p2)*crossx(p1,q2,p1,p2)<0&&crossx(q1,p1,q1,q2)*crossx(q1,p2,q1,q2)==0)||
            (crossx(p1,q1,p1,p2)*crossx(p1,q2,p1,p2)==0&&crossx(q1,p1,q1,q2)*crossx(q1,p2,q1,q2)<0))
        return true;//存在一端點在另外一條線段上而不是端點處的相交 
    else if(crossx(p1,q1,p1,p2)*crossx(p1,q2,p1,p2)==0&&crossx(q1,p1,q1,q2)*crossx(q1,p2,q1,q2)==0){//共線 
        if(isch(p1,p2,q1,q2)) return false; 
        else if(issame(p1,p2,q1,q2)){//存在一組端點重合 
            flag=1;//不是正常相交,須要本身算,以後就不算 
            return true;
        }
        else return false;
    }
    return false;       
}
Point getCrossPoint(Segment s1,Segment s2){
    Vector base;//向量
    base=s2.p2-s2.p1; 
    double d1=fabs(cross(base,s1.p1-s2.p1));
    double d2=fabs(cross(base,s1.p2-s2.p1));//算三角形面積,只是沒有除以2 
    double t=d1/(d1+d2);//面積之比等於線段之比,可理解爲t=AO/(AO+BO) 
    return s1.p1+(s1.p2-s1.p1)*t;//經過A點座標加上向量OA而後求得O點座標 
}
int main() {
    Segment s1,s2;
    Point p;
    while(~scanf("%lf%lf%lf%lf",&s1.p1.x,&s1.p1.y,&s1.p2.x,&s1.p2.y)) {
        flag=0;
        scanf("%lf%lf%lf%lf",&s2.p1.x,&s2.p1.y,&s2.p2.x,&s2.p2.y);
        if(!judge(s1.p1,s1.p2,s2.p1,s2.p2)) printf("none\n");
        else {
            if(!flag) {
                p=getCrossPoint(s1,s2);//交點座標 
                printf("%lf %lf\n",p.x,p.y);
            }
        }
    }
}
//簡單板子,只判斷是否相交重合也看成相交
const double eps=1e-10;
struct point {
    double x, y;
};
double min(double a, double b) {
    return a < b ? a : b;
}
double max(double a, double b) {
    return a > b ? a : b;
}
bool inter(point a, point b, point c, point d) {
    if ( min(a.x, b.x) > max(c.x, d.x) ||    min(a.y, b.y) > max(c.y, d.y) ||    min(c.x, d.x) > max(a.x, b.x) ||    min(c.y, d.y) > max(a.y, b.y) ) return 0;
    double h, i, j, k;
    h = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
    i = (b.x - a.x) * (d.y - a.y) - (b.y - a.y) * (d.x - a.x);
    j = (d.x - c.x) * (a.y - c.y) - (d.y - c.y) * (a.x - c.x);
    k = (d.x - c.x) * (b.y - c.y) - (d.y - c.y) * (b.x - c.x);
    return h * i <= eps && j * k <= eps;
}

ACM計算幾何板子彙總

/*==================================================*\ 
| Liuctic的計算幾何庫 | p-Lpoint ln,l - Lline ls - Llineseg lr - Lrad
| 求平面上兩點之間的距離    p2pdis
| 返回(P1-P0)*(P2-P0)的叉積。   xmulti
| 肯定兩條線段是否相交    lsinterls
| 判斷點p是否在線段l上    ponls
| 判斷兩個點是否相等     Euqal_Point
| 線段非端點相交      lsinterls_A
| 判斷點q是否在多邊形Polygon內  pinplg
| 多邊形的面積      area_of_polygon
| 解二次方程      Ax^2+Bx+C=0 equa
| 點到直線距離      p2lndis
| 直線與圓的交點,已知直線與圓相交 lncrossc
| 點是否在射線的正向     samedir
| 射線與圓的第一個交點    lrcrossc
| 求點p1關於直線ln的對稱點p2   mirror
| 兩直線夾角(弧度)                  angle_LL
\*==================================================*/
#define infinity 1e20
#define EP 1e-10
const int MAXV = 300 ;
const double PI = 2.0*asin(1.0);    //高精度求PI
struct Lpoint {double x,y;};         //點
struct Llineseg {Lpoint a,b;};      //線段
struct Ldir {double dx,dy;};          //方向向量
struct Lline {Lpoint p;Ldir dir;}; //直線
struct Lrad {Lpoint Sp;Ldir dir;}; //射線
struct Lround {Lpoint co;double r;};//圓
// 求平面上兩點之間的距離
double p2pdis(Lpoint p1,Lpoint p2) {
    return (sqrt((p1.x-p2.x) * (p1.x-p2.x) + (p1.y-p2.y) * (p1.y-p2.y)));
}
/*******************************************************
| (P1-P0)*(P2-P0)的叉積  若結果爲正,則<P0,P1>在<P0,P2>的順時針方向;  若
爲0則<P0,P1><P0,P2>共線;  若爲負則<P0,P1>在<P0,P2>的在逆時針方向;
能夠根據這個函數肯定兩條線段在交點處的轉向,  好比肯定p0p1和p1p2在p1處是左轉仍是右轉,
只要求  (p2-p0)*(p1-p0),若<0則左轉,>0則右轉,=0則共線
********************************************************/
double xmulti(Lpoint p1,Lpoint p2,Lpoint p0){
    return((p1.x-p0.x) * (p2.y-p0.y) -(p2.x-p0.x) * (p1.y-p0.y));
}
// 肯定兩條線段是否相交
double mx(double t1,double t2) {
    if(t1>t2) return t1;
    return t2;
}
double mn(double t1,double t2) {
    if(t1<t2) return t1;
    return t2;
}
int lsinterls(Llineseg u,Llineseg v) {
    return( (mx(u.a.x,u.b.x)>=mn(v.a.x,v.b.x))&&
            (mx(v.a.x,v.b.x)>=mn(u.a.x,u.b.x))&&
            (mx(u.a.y,u.b.y)>=mn(v.a.y,v.b.y))&&
            (mx(v.a.y,v.b.y)>=mn(u.a.y,u.b.y))&&
            (xmulti(v.a,u.b,u.a)*xmulti(u.b,v.b,u.a)>=0)&&
            (xmulti(u.a,v.b,v.a)*xmulti(v.b,u.b,v.a)>=0)
    );
}
//判斷點p是否在線段l上
int ponls(Llineseg l,Lpoint p) {
    return( (xmulti(l.b,p,l.a)==0) &&
            ( ((p.x-l.a.x)*(p.x-l.b.x)<0 ) ||
              ((p.y-l.a.y)*(p.y-l.b.y)<0 )) );
}
//判斷兩個點是否相等
int Euqal_Point(Lpoint p1,Lpoint p2) {
    return((fabs(p1.x-p2.x)<EP)&&(fabs(p1.y-p2.y)<EP));
}
//線段相交判斷函數 當且僅當u,v相交而且交點不是u,v的端點時函數爲true;
int lsinterls_A(Llineseg u,Llineseg v) {
    return((lsinterls(u,v)) && (!Euqal_Point(u.a,v.a))&&
           (!Euqal_Point(u.a,v.b)) && (!Euqal_Point(u.b,v.a))&&
           (!Euqal_Point(u.b,v.b)));
}
/*===============================================
|  判斷點q是否在多邊形內    其中多邊形是任意的凸或凹多邊形,
Polygon中存放多邊形的逆時針頂點序列
================================================*/
int pinplg(int vcount,Lpoint Polygon[],Lpoint q) {
    int c=0,i,n;
    Llineseg l1,l2;
    l1.a=q;
    l1.b=q;
    l1.b.x=infinity;
    n=vcount;
    for (i=0; i<vcount; i++) {
        l2.a=Polygon[i];
        l2.b=Polygon[(i+1)%n];
        if ( (lsinterls_A(l1,l2))||
        (
        (ponls(l1,Polygon[(i+1)%n]))&&       
        (       
        (!ponls(l1,Polygon[(i+2)%n]))&&       
        (xmulti(Polygon[i],Polygon[(i+1)%n],l1.a) *        
        xmulti(Polygon[(i+1)%n],Polygon[(i+2)%n],l1.a)>0)
        ||       
        (ponls(l1,Polygon[(i+2)%n]))&&       
        (xmulti(Polygon[i],Polygon[(i+2)%n],l1.a) *        
        xmulti(Polygon[(i+2)%n],Polygon[(i+3)%n],l1.a)>0)       
        ) 
        ) 
        ) 
        c++;
    }
    return(c%2!=0);
}
/*==================================================*\ 
| 計算多邊形的面積
| 要求按照逆時針方向輸入多邊形頂點
| 能夠是凸多邊形或凹多邊形
\*==================================================*/
double areaofp(int vcount,double x[],double y[],Lpoint plg[]) {
    int i;
    double s;
    if (vcount<3) return 0;
    36    lncrossc(ln2,Y,p1,p2);
    s=plg[0].y*(plg[vcount-1].x-plg[1].x);
    for (i=1; i<vcount; i++)      s+=plg[i].y*(plg[(i-1)].x-plg[(i+1)%vcount].x);
    return s/2;
}
/*********************\ 
| 解二次方程 Ax^2+Bx+C=0 返回-1表示無解 返回1 表示有解
\*********************/
int equa(double A,double B,double C,double& x1,double& x2) {
    double f=B*B-4*A*C;
    if(f<0) return -1;
    x1=(-B+sqrt(f))/(2*A);
    x2=(-B-sqrt(f))/(2*A);
    return 1;
}
//計算直線的通常式 Ax+By+C=0
void format(Lline ln,double& A,double& B,double& C) {
    A=ln.dir.dy;
    B=-ln.dir.dx;
    C=ln.p.y*ln.dir.dx-ln.p.x*ln.dir.dy;
}
//點到直線距離
double p2ldis(Lpoint a,Lline ln) {
    double A,B,C;
    format(ln,A,B,C);
    return(fabs(A*a.x+B*a.y+C)/sqrt(A*A+B*B));
}
//直線與圓的交點,已知直線與圓相交
int lncrossc(Lline ln,Lround Y,Lpoint& p1,Lpoint& p2) {
    double A,B,C,t1,t2;
    int zz=-1;
    format(ln,A,B,C);
    if(fabs(B)<1e-8)  {
        p1.x=p2.x=-1.0*C/A;
        zz=equa(1.0,-2.0*Y.co.y,Y.co.y*Y.co.y
                +(p1.x-Y.co.x)*(p1.x-Y.co.x)-Y.r*Y.r,t1,t2);
        p1.y=t1;
        p2.y=t2;
    }  else if(fabs(A)<1e-8)  {
        p1.y=p2.y=-1.0*C/B;
        zz=equa(1.0,-2.0*Y.co.x,Y.co.x*Y.co.x
                +(p1.y-Y.co.y)*(p1.y-Y.co.y)-Y.r*Y.r,t1,t2);
        p1.x=t1;
        p2.x=t2;
    }  else  {
        zz=equa(A*A+B*B,2.0*A*C+2.0*A*B*Y.co.y
                -2.0*B*B*Y.co.x,B*B*Y.co.x*Y.co.x+C*C+2*B*C*Y.co.y
                +B*B*Y.co.y*Y.co.y-B*B*Y.r*Y.r,t1,t2);
        p1.x=t1,p1.y=-1*(A/B*t1+C/B);
        p2.x=t2,p2.y=-1*(A/B*t2+C/B);
    }
    return 0;
}
//點是否在射線的正向
bool samedir(Lrad ln,Lpoint P) {
    double ddx,ddy;
    ddx=P.x-ln.Sp.x;
    ddy=P.y-ln.Sp.y;
    if((ddx*ln.dir.dx>0||fabs(ddx*ln.dir.dx)<1e-7)     
    &&(ddy*ln.dir.dy>0||(fabs(ddy*ln.dir.dy)<1e-7)))     
    return true;
    else return false;
}
//射線與圓的第一個交點 已經肯定射線所在直線與圓相交返回-1表示不存正向交點 ,不然返回1
int lrcrossc(Lrad ln, Lround Y, Lpoint& P) {
    Lline ln2;
    Lpoint p1,p2;
    int res=-1;
    double dis=1e20;
    ln2.p=ln.Sp,ln2.dir=ln.dir;
    if(samedir(ln,p1)) {
        res=1;
        if(p2pdis(p1,ln.Sp)<dis) {
            dis=p2pdis(p1,ln.Sp);
            P=p1;
        }
    }
    if(samedir(ln,p2)) {
        res=1;
        if(p2pdis(p2,ln.Sp)<dis) {
            dis=p2pdis(p2,ln.Sp);
            P=p2;
        }
    }
    return res;
}
//求點p1關於直線ln的對稱點p2
Lpoint mirror(Lpoint P,Lline ln) {
    Lpoint Q;
    double A,B,C;
    format(ln,A,B,C);
    Q.x=((B*B-A*A)*P.x-2*A*B*P.y-2*A*C)/(A*A+B*B);
    Q.y=((A*A-B*B)*P.y-2*A*B*P.x-2*B*C)/(A*A+B*B);
    return Q;
}
//兩直線夾角(弧度)
double angle_LL(Lline line1, Lline line2) {
    double A1, B1, C1;
    format(line1, A1, B1, C1);
    double A2, B2, C2;
    format(line2, A2, B2, C2);
    if( A1*A2+B1*B2 == 0 ) return PI/2.0; // 垂直
    else {
        double t = fabs((A1*B2-A2*B1)/(A1*A2+B1*B2));
        return atan(t);
    }
}

6、圖論

最短路徑

Dijkstra算法

最樸素(鄰接矩陣)

#define max ....
#define INF ....
int sweight[max]={},map[max][max],spath[max];
void Dijkstra(int v0){
    int i,j,v,minweight;
    char wfound[max]={0};
    for (i=1;i<=n;i++){
        sweight[i]=map[v0][i];
        spath[i]=v0;
    }
    sweight[v0]=0;
    wfound[v0]=1;
    for (i=1;i<=n-1;i++){//i從0取仍是從1取,取決於頂點編號
        minweight=INFINITY;
        for (j=1;j<=n;j++)
            if (!wfound[j]&&(sweight[j]<minweight)){
                v=j;
                minweight=sweight[j];
            }
        wfound[v]=1;
        for (j=1;j<=n;j++){
            if (!wfound[j]&&(minweight+map[v][j]<sweight[j])){
                sweight[j]=minweight+map[v][j];
                spath[j]=v;
            }   
        }           
    }
}
//別忘了map數組初始化爲最大值INF

優先隊列優化

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define maxn 10005
#define inf 0x3f3f3f3f
using namespace std;
int dis[maxn];
int judge[maxn];
int n, m, s, t, u; 
struct node{
    int v, w;
    node() {}
    node(int v, int w) : v(v), w(w) {}
    bool operator < (const node &a) const {
        return w > a.w;
    } 
}tmp;
vector<struct node> Adj[maxn];
void Dijkstra(int s)
{
    memset(dis,0x3f,sizeof(dis)); // 賦最大值 
    dis[s] = 0;
    priority_queue<struct node> Q;
    tmp.v = s; tmp.w = 0;
    Q.push(tmp);
    node nd;// 臨時存儲
    
    while(!Q.empty()) {
        nd = Q.top(); Q.pop();
        if(judge[nd.v]) continue;
        judge[nd.v] = 1;
        for(int i = 0; i < Adj[nd.v].size(); i++)
        {
            int j = Adj[nd.v][i].v;
            int k = Adj[nd.v][i].w;
            if(nd.w + k < dis[j] && !judge[j]) {// 鬆弛
                dis[j] = nd.w + k;
                tmp.v = j, tmp.w = dis[j];
                Q.push(tmp);
            }
         } 
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i = 1, u, v, w; i <= m; i++)
    {
        scanf("%d%d%d",&u, &v, &w);
        tmp.v = v;tmp.w = w;
        Adj[u].push_back(tmp);
        tmp.v = u;tmp.w = w;
        Adj[v].push_back(tmp);
    }
    Dijkstra(s);
    printf("%d\n",dis[t]);
    return 0;
}

SPFA

鄰接矩陣

#include<cstdio>
#include<cstring>
#include<queue>
#define max 2600
#define inf 1000000000
int map[max][max],n,m,s,t;
int dis[max],visit[max],num[max];//num[max]用來保存入隊次數,visit[max]判斷是否入隊; 
std::queue<int> q;
void init(){
    memset(visit,0,sizeof(visit));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) map[i][j]=0;
            else map[i][j]=inf;
        }
    }
    for(int i=1;i<=n;i++){
        if(i==s) dis[i]=0;
        else dis[i]=inf;
    }
    while(!q.empty()) q.pop();
}
void spfa(){
    int temp,i;
    q.push(s);
    visit[s]=1;
    while(!q.empty()){
        temp=q.front();
        q.pop();
        visit[temp]=0;
        for(i=1;i<=n;i++){
            if(map[temp][i]!=inf){
                if(dis[i]>dis[temp]+map[temp][i]){
                    dis[i]=dis[temp]+map[temp][i];
                    if(!visit[i]){
                        q.push(i);
                        visit[i]=1;// 這後面能夠經過num[i]判斷是否存在負環
                    }
                }
            }
        }
    }
}
int main(){
    int a,b,c;
    scanf("%d%d%d%d",&n,&m,&s,&t);// 若是沒有輸入s,記得本身給出s
    init();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        map[a][b]=map[b][a]=c;
    }
    spfa();
    printf("%d",dis[t]);
}

鄰接表

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define max 2000
#define inf 1000000000
std::queue<int> q;
int n,m,s,t;
int head[max],dis[max],visit[max],cnt=0,num[max];
//head的下標表示節點的編號,存的是以這個節點爲起點的添加進來的邊的最後一個編號 
struct edge{
    int to;
    int c;
    int next;//next表示這條邊指向的下一條相同起點的邊的編號
}e[max];
void add(int u,int v,int w){
    e[cnt].c=w;
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void insert(int u,int v,int w){
    add(u,v,w);
    add(v,u,w);
}
void init(){
    memset(visit,0,sizeof(visit));
    memset(head,-1,sizeof(head));
    //memset(num,0,sizeof(num));
}
void spfa(){
    int tmp,i;
    while(!q.empty()) q.pop();
    //s=1;
    for(i=1;i<=n;i++){
        if(i==s) dis[i]=0;
        else dis[i]=inf;
    }
    visit[s]=1;
    q.push(s);
    while(!q.empty()){
        tmp=q.front();
        q.pop();
        visit[tmp]=0;
        for(i=head[tmp];~i;i=e[i].next){
            int to=e[i].to;////i爲邊的編號,i鏈接tmp和to兩個頂點 
            if(dis[to]>dis[tmp]+e[i].c){
                dis[to]=dis[tmp]+e[i].c;
                if(!visit[to]){
                    visit[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
int main(){
    int u,v,w;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    init();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        insert(u,v,w); 
    }
    spfa();
    printf("%d",dis[t]);
}

Floyd

鄰接矩陣

for(int k=1; k<=n; k++)
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(e[i][j]>e[i][k]+e[k][j])
                e[i][j]=e[i][k]+e[k][j];//e[i][j]表明i到j的最短路徑

最大流

Edmonds-Karp算法

鄰接矩陣

#include<...>
#define max ...
int n,m,map[max][max];// n爲點數,m爲邊數,map裏存容量
int path[max],flow[max];// flow[]存流過當前頂點的流量,path[]存當前頂點的前序頂點
int start=0,end=n;
std:queue<int> q;
int EK_bfs() {
    int i,t;
    while(!q.empty()) q.pop();
    memset(path,-1,sizeof(path));
    path[start]=0;
    flow[start]=INF;
    q.push(start);
    while(!q.empty()) {
        t=q.front();
        q.pop();
        if(t==end) break;
        for(i=1; i<=n; i++) {
            if(i!=start && path[i]==-1 && map[t][i]) {
                flow[i]=flow[t]<map[t][i]?flow[t]:map[t][i];// 更新流量,但不能超過容量,取更小 
                q.push(i);
                path[i]=t;
            }
        }
    }
    if(path[end]==-1) return -1;
    return flow[n];
}
int EK() {
    int max_flow=0,step,now,pre;
    while((step=EK_bfs())!=-1) {
        max_flow+=step;
        now=end;
        while(now!=start) {
            pre=path[now];
            map[pre][now]-=step;// 更新殘餘網絡,由於流更新後,殘餘網絡也更新了 
            map[now][pre]+=step;
            now=pre;
        }
    }
    return max_flow;
}
int main(){
    memset(map,0,sizeof(map));
    //輸入map[][]
    printf("%d",EK());
}

Dinic算法

鄰接表

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define INF 2147483647
#define max 1000
int min(int a,int b){
    if(a<b) return a;
    return b;
}
struct Edge{
    int v;
    int c;
    int next;
}e[max];
int head[max],e_num=-1;
int n,m,S,T;

void add(int u,int v,int c){
    e_num++;
    e[e_num].v=v;
    e[e_num].c=c;
    e[e_num].next=head[u];
    head[u]=e_num;
}
void insert(int u,int v,int c){
    add(u,v,c);
    add(v,u,0);
}
int depth[max];// 層次網絡 
bool bfs()
{
    std::queue<int> q;//定義一個bfs尋找分層圖時的隊列
    while (!q.empty()) q.pop();
    memset(depth,-1,sizeof(depth));
    depth[S]=0;//源點深度爲0
    q.push(S);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(e[i].c>0&&depth[v]==-1){
                q.push(v);
                depth[v]=depth[u]+1;
            }
        }
    }
    return (depth[T]!=-1);
}
int dfs(int u,int flow){        //flow表示當前搜索分支的流量上限
    if(u==T){
        return flow;
    }
    int res=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(e[i].c>0&&depth[u]+1==depth[v]){
            int tmp=dfs(v,min(flow,e[i].c));    // 遞歸計算頂點 v,用 c(u, v) 來更新當前流量上限
            flow-=tmp;
            e[i].c-=tmp;
            res+=tmp;
            e[i^1].c+=tmp;      // 修改反向弧的容量
            if(flow==0){        // 流量達到上限,沒必要繼續搜索了
                break;
            }
        }
    }
    if(res==0){     // 當前沒有通過頂點 u 的可行流,再也不搜索頂點 u
        depth[u]=-1;
    }
    return res;
}
int dinic(){        // 函數返回值就是最大流的結果
    int res=0;
    while(bfs()){
        res+=dfs(S,INF);    // 初始流量上限爲 INF
    }
    return res;
}
int main(){
    scanf("%d%d",&m,&n);//m爲邊
    memset(head,-1,sizeof(head));
    for(int i=0;i<m;i++){
        int u,v,flow;
        scanf("%d%d%d",&u,&v,&flow);
        insert(u,v,flow);
    }
    //給出S和T
    printf("%d",dinic());
    return 0;
}

Sap算法

鄰接矩陣

#include<iostream>
#include<cstdio>
#include<cstring>
 
using namespace std;
#define N 1002
#define INF 0x3f3f3f3f
 
int e[N][N];
int pre[N]; //記錄當前點的前驅。
int d[N];   //記錄距離標號  i-t距離的下界。
int num[N];  //gap優化,每一個距離下標下的節點編號有多少個,爲0的話,說明s-t不連通
 
int SAP(int s,int t){
    memset(pre,-1,sizeof(pre));
    memset(d,0,sizeof(d));
    memset(num,0,sizeof(num));
    num[0]=t;
    int v,u=pre[s]=s,flow=0,aug=INF;
    while(d[s]<t){  //else 殘量網絡中不存在s-t路。
        //尋找可行弧
        for(v=1;v<=t;v++){
            if(e[u][v]>0&&d[u]==d[v]+1){
                break;
            }
        }
        if(v<=t){
            pre[v]=u;
            u=v;
            if(v==t){
                aug=INF;
                //尋找當前找到路徑上的最大流
                for(int i=v;i!=s;i=pre[i]){
                    if(aug>e[pre[i]][i]) aug=e[pre[i]][i];
                }
                flow+=aug;
                //更新殘留網絡。
                for(int i=v;i!=s;i=pre[i]){
                    e[pre[i]][i]-=aug;
                    e[i][pre[i]]+=aug;
                }
                u=s;        //從源點開始繼續搜。
            }
        }else{
            //找不到可行弧
            int minlevel=t;
            //尋找與當前點鏈接的最小的距離標號。
            for(v=1;v<=t;v++){
                if(e[u][v]>0&&minlevel>d[v]){
                    minlevel=d[v];
                }
            }
            num[d[u]]--;            //當前標號的數目減一
            if(!num[d[u]]) break; //出現斷層。
            d[u]=minlevel+1;
            num[d[u]]++;
            u=pre[u];
        }
    }
    return flow;
}
 
int main()
{
    int n,m,u,v,w;      //m,邊數,n,節點數.
    while(~scanf("%d%d",&n,&m);){
        memset(e,0,sizeof(e));
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            e[u][v]+=w;
            e[v][u]+=w;
        }
        printf("%d\n",SAP(1,n));
    }
    return 0;
}

鄰接表

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
 
const int INF = 0x3f3f3f3f;
const int MAX_N=5000;     //頂點數上限
const int MAX_M=500000;    //總的邊數上限
 
struct edge{
    int v,c,next;       //v指另外一個頂點,c表示容量。
}e[MAX_M];
 
int p[MAX_N],eid;
 
void init(){
    memset(p,-1,sizeof(p));
    eid=0;
}
 
void insert(int u,int v,int c){ //插入一條從u向v,容量爲c的弧。
    e[eid].v=v;
    e[eid].next=p[u];
    e[eid].c=c;
    p[u]=eid++;
}
 
void addedge(int u,int v,int c){ //用insert插入網絡中的弧
    insert(u,v,c);
    insert(v,u,0);                  //插入一條反方向,當前容量爲0的弧
}
 
int num[MAX_N];
int d[MAX_N];
int cur[MAX_N];
int pre[MAX_N];
 
int SPA(int s,int t,int n){           //S是源點,T是匯點。
    int cur_flow,flow_ans=0,u,tmp,neck,i;
    memset(num,0,sizeof(num));
    memset(d,0,sizeof(d));
    memset(pre,-1,sizeof(pre));
    for(i=1;i<=n;i++){
        cur[i]=p[i];
    }
    num[0]=n;
    u=s;
    while(d[s]<n){
        if(u==t){
            cur_flow=INF;
            for(i=s;i!=t;i=e[cur[i]].v){
                if(cur_flow>e[cur[i]].c){   //增廣成功,尋找瓶頸邊
                    neck=i;
                    cur_flow=e[cur[i]].c;
                }
            }
            for(i=s;i!=t;i=e[cur[i]].v){
                tmp=cur[i];
                e[tmp].c-=cur_flow;
                e[tmp^1].c+=cur_flow;
            }
            flow_ans+=cur_flow;
            u=neck; //下次增廣從瓶頸邊開始
        }
        //尋找可行弧
        for(i=cur[u];i!=-1;i=e[i].next){
            if(e[i].c&&d[u]==d[e[i].v]+1) break;
        }
        if(i!=-1){
            cur[u]=i;
            pre[e[i].v]=u;
            u=e[i].v;
        }else{
            if(0==--num[d[u]]) break;
            cur[u]=p[u];
            for(tmp=n,i=p[u];i!=-1;i=e[i].next){
                if(e[i].c){
                    tmp=min(tmp,d[e[i].v]);
                }
            }
            d[u]=tmp+1;
            num[d[u]]++;
            if(u!=s) u=pre[u];
        }
    }
    return flow_ans;
}
 
int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=0;i<m;i++){
            int u,v,flow;
            scanf("%d%d%d",&u,&v,&flow);
            addedge(u,v,flow);
        }
        printf("%d",SPA(1,n,n));
        //cout<<SPA(1,n,n)<<endl;
    }
    return 0;
}

轉載於: https://blog.csdn.net/qq_40679299/article/details/81108783

hlpp算法

鄰接表

#include<bits/stdc++.h>
#define re register
#define il inline
#define inc(i,j,k) for(re int i=j;i<=k;++i)
#define ra(i,u) for(re int i=head[u];i!=-1;i=a[i].nxt)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxm=120010;
const int maxn=2010;
struct node
{
    int to,nxt,flow;
}a[maxm<<1];
int head[maxn],gap[maxn],h[maxn],e[maxn];
bool vis[maxn];
int cnt=-1,n,m,st,ed;
struct cmp {il bool operator () (int x,int y)const{return h[x]<h[y];}};
priority_queue<int,vector<int>,cmp> pq;
queue<int> q;
il void add(int u,int v,int w)
{
    a[++cnt].to=v;
    a[cnt].nxt=head[u];
    a[cnt].flow=w;
    head[u]=cnt;
}
il bool bfs()
{
    memset(h,inf,sizeof(h));
    h[ed]=0;
    q.push(ed);
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        ra(i,t)
        {
            int v=a[i].to;
            if(a[i^1].flow && h[v]>h[t]+1)
            {
                h[v]=h[t]+1;
                q.push(v);
            }
        }
    }
    return h[st]!=inf;
}
il void push(int u)
{
    ra(i,u)
    {
        int v=a[i].to;
        if((a[i].flow) && (h[v]+1==h[u]))
        {
            int df=min(e[u],a[i].flow);
            a[i].flow-=df;
            a[i^1].flow+=df;
            e[u]-=df;
            e[v]+=df;
            if((v!=st)&&(v!=ed)&&(!vis[v]))
            {
                pq.push(v);
                vis[v]=1;
            }
            if(!e[u])break;
        }
    }
}
il void relabel(int u)
{
    h[u]=inf;
    ra(i,u)
    {
        int v=a[i].to;
        if((a[i].flow)&&(h[v]+1<h[u]))h[u]=h[v]+1;
    }
}
inline int hlpp()
{
    if(!bfs())return 0;
    h[st]=n;
    memset(gap,0,sizeof(gap));
    inc(i,1,n) if(h[i]!=inf)gap[h[i]]++;
    ra(i,st)
    {
        int v=a[i].to;
        if(int f=a[i].flow)
        {
            a[i].flow-=f;a[i^1].flow+=f;
            e[st]-=f;e[v]+=f;
            if(v!=st&&v!=ed&&!vis[v])
            {
                pq.push(v);
                vis[v]=1;
            }
        }
    }
    while(!pq.empty())
    {
        int t=pq.top();pq.pop();
        vis[t]=0;push(t);
        if(e[t])
        {
            gap[h[t]]--;
            if(!gap[h[t]])
            {
                inc(v,1,n)
                {
                    if(v!=st&&v!=ed&&h[v]>h[t]&&h[v]<n+1)
                    {
                        h[v]=n+1;
                    }
                }
            }
            relabel(t);gap[h[t]]++;
            pq.push(t);vis[t]=1;
        }
    }
    return e[ed];
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    st=1,ed=n;
    inc(i,1,m)
    {
        int x,y;
        ll f;
        scanf("%d%d%lld",&x,&y,&f);
        add(x,y,f);
        add(y,x,0);
    }
    ll maxf=hlpp();
    printf("%lld",maxf);
    return 0;
}

轉載於: http://www.javashuo.com/article/p-djiuvzwa-bq.html

ISAP算法

鄰接表

#include <iostream>
#include <cstdio>
#include <climits>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef  struct {ll v,next,val;} edge;
const int MAXN=20010;
const int MAXM=500010;
edge e[MAXM];
ll p[MAXN],eid;
inline void init(){memset(p,-1,sizeof(p));eid=0;}
//有向
inline void insert1(ll from,ll to,ll val)
{
    e[eid].v=to;e[eid].val=val;
    e[eid].next=p[from];
    p[from]=eid++;
    swap(from,to);
    e[eid].v=to;e[eid].val=0;
    e[eid].next=p[from];
    p[from]=eid++;
}
//無向
inline void insert2(ll from,ll to,ll val)
{
    e[eid].v=to;e[eid].val=val;
    e[eid].next=p[from];
    p[from]=eid++;
    swap(from,to);
    e[eid].v=to;e[eid].val=val;
    e[eid].next=p[from];
    p[from]=eid++;
}
ll n,m;//n爲點數 m爲邊數
ll h[MAXN];
ll gap[MAXN];
ll source,sink;
inline ll dfs(ll pos,ll cost)
{
    if (pos==sink) return cost;
 
    ll j,minh=n-1;
    ll lv=cost,d;
    for (j=p[pos];j!=-1;j=e[j].next)
    {
        ll v=e[j].v,val=e[j].val;
        if(val>0)
        {
            if (h[v]+1==h[pos])
            {
                if (lv<e[j].val) d=lv;
                else d=e[j].val;
 
                d=dfs(v,d);
                e[j].val-=d;
                e[j^1].val+=d;
                lv-=d;
                if (h[source]>=n) return cost-lv;
                if (lv==0) break;
            }
            if (h[v]<minh)    minh=h[v];
        }
    }
    if (lv==cost)
    {
        --gap[h[pos]];
        if (gap[h[pos]]==0) h[source]=n;
        h[pos]=minh+1;
        ++gap[h[pos]];
    }
    return cost-lv;
}
 
ll isap(ll st,ll ed)
{
 
    source=st;sink=ed;
    ll ret=0;
    memset(gap,0,sizeof(gap));
    memset(h,0,sizeof(h));
    gap[st]=n;
    while (h[st]<n)
    {
        ret+=dfs(st,INT_MAX);
    }
    return ret;
}
int main()
{   ll sp,tp;
    //freopen("in.txt","r",stdin);
    cin >> n >>m;//>> sp>> tp;

        init();
        for(ll i=0;i<m;i++)
        {
            ll u,v,c;
            scanf("%lld%lld%lld",&u,&v,&c);
            insert2(u,v,c);
        }
        printf("%lld\n",isap(1,n));  //這裏是從1走到n
 
    return 0;
}

二分圖

匈牙利算法

鄰接矩陣

#include<iostream>
#include<cstdio>
#include<cstring>

int ans=0,n;
int link[10005],use[10005],map[10005][10005];//map數組爲鄰接矩陣,use表示當前點是否匹配,link[i]表示與頂點i所連的點
bool dfs(int x) {
    for(int i=1;i<=n;i++){
        if(!use[i]&&map[x][i]) { //若不在交替路中
            use[i] = 1;//則加入交替路
            if(!link[i] || dfs(link[i])) {
                link[i] = x;
                return true;
            }
        }
    }
    return false;
}
void xyl( ) {
    memset(link, 0, sizeof(link));
    for(int i=1;i<=n;i++) {
        memset(use,0,sizeof(use));
        if(dfs(i)) ans++;
    }
}
int main() {
    int i;
    int a[10005],b[10005];
    while(~scanf("%d",&n)) {
        ans=0;
        memset(map, false, sizeof(map));
        /*for(i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            map[i][a[i]]=true;
        }
        for(i=1; i<=n; i++) {
            scanf("%d",&b[i]);
            map[b[i]][i]=true;
        }*/ //輸入map
        xyl();
        printf("%d\n",ans);// ans爲最大匹配數
    }
}

鄰接表

#include<cstring>
#include<iostream>
using namespace std;
const int maxn=50010;//邊數的最大值
struct Edge
{
    int to,next;
}edge[maxn];
//to 是該邊指向的點 next是這個點上次用的邊的編號,用來找到這個點上次和其餘點維持的邊關係 edge的下標表明邊的編號
int head[maxn],tot;
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}//初始化函數
void addedge(int u,int v)
{
    edge[tot].to=v;//對邊進行編號
    edge[tot].next=head[u];//將U這個點上一次鏈接的點記錄若是沒有即爲-1
    head[u]=tot++;//等於邊的編號,以後edge[head[u]]便可調用這個邊
}//加邊函數
int linker[maxn];
bool used[maxn];
int n;
bool dfs(int u)
{
    for(int i=head[u];i!=-1;i = edge[i].next)//順着邊過去,一直遍歷和這個點鏈接過的點和邊
    {
        int v=edge[i].to;
        if(!used[v])
        {
            used[v]=true;
            if(linker[v]==-1 || dfs(linker[v]))
            {
                linker[v]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(int u=1;u<=n;u++)
    {
        memset(used,false,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
int main ()
{
    //int a[maxn],b[maxn];
    while(~scanf("%d",&n)){
        init();
        /*for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            if(a[i]!=0) 
                addedge(i,a[i]);
            //addedge(a[i],i);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&b[i]);
            if(b[i]!=0)
                addedge(b[i],i);
            //addedge(i,b[i]);
        }*/
        printf("%d\n",hungary());
    }   
}

HK算法

鄰接矩陣

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 2147483647

int ans=0,n,dis;
int dx[10005],dy[10005],cx[10005],cy[10005];
bool used[10005];
int map[10005][10005];//map數組爲鄰接矩陣,use表示當前點是否匹配,link[i]表示與頂點i所連的點
bool searchP()
{
    std::queue<int> q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=n;i++){
        if(cx[i]==-1) {q.push(i);dx[i]=0;}//對於未遍歷的點入隊
    }
    //準備分層
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        if(dx[u]>dis) break;//若是目前的層次大於最小增廣長度,那麼退出
        for(int j=1;j<=n;j++)//對於一切可能的點遍歷
        {
            if(map[u][j]==true&&dy[j]==-1){//只對未分層的點遍歷
                dy[j]=dx[u]+1;
                if(cy[j]==-1) dis=dy[j];
                else{
                    dx[cy[j]]=dy[j]+1;
                    q.push(cy[j]);
                }
            }
        }
    }
    return dis!=inf;
}
bool findpath(int x)
{
    for(int j=1;j<=n;j++)
    {
        if(!used[j]&&map[x][j]&&dy[j]==dx[x]+1)//符合繼續搜索的條件有三個:未訪問過,圖上聯通和層次符合
        {
            used[j]=1;
            if(cy[j]!=-1&&dis==dy[j]) continue;//若是下一個點仍是匹配點且目前已經到達增廣最小層次,不須要擴展了
            if(cy[j]==-1||findpath(cy[j]))
            {
                cy[j]=x;cx[x]=j;
                return true;
            }
        }
    }
    return false;
}
int hk()
{
    int ans=0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    while(searchP())
    {
        memset(used,0,sizeof(used));
        for(int i=1;i<=n;i++){
            if(cx[i]==-1)
            {
                if(findpath(i)) ans++;
            }
        }
    }
    return ans;
}
int main( ) {
    int i;
    int a[10005],b[10005];
    while(~scanf("%d",&n)) {
        memset(map, false, sizeof(map));
        /*for(i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            map[i][a[i]]=true;
        }
        for(i=1; i<=n; i++) {
            scanf("%d",&b[i]);
            map[b[i]][i]=true;
        }*/ //輸入map
        printf("%d\n",hk());
    }
}

二分圖斷定(染色法)

#include <cstdio>
#include <vector>
 
const int MAX_V = 1000 + 7; // 定點最大個數
using namespace std;
 
vector<int> g[MAX_V];   // 鄰接表
int color[MAX_V];       // 頂點i的顏色
 
bool dfs(int v, int c)
{
    color[v] = c;       // 把頂點染成顏色c
    for(int i=0; i<g[v].size(); i++)    // 查詢與這個頂點相鄰的頂點
    {
        if(color[g[v][i]] == c)         // 若是相鄰的頂點也爲c,則染色不成功
            return false;
        if(color[g[v][i]] == 0 && !dfs(g[v][i], -c))    // 相鄰的頂點沒有染色,就染成-c,並判斷可否染色成功
            return false;
    }
    return true;
}
 
int main()
{
    int V;                  // 頂點個數
    int a, b;       // a->b有一條邊
    scanf("%d", &V);
    for(int i=0; i<MAX_V; i++)
        g[i].clear();   // 清空鄰接表
    while(scanf("%d%d", &a, &b) != EOF)
    {
        g[a].push_back(b);  // 無向圖
        g[b].push_back(a);
    }
    for(int i=0; i<V; i++)
    {
        if(color[i] == 0)
        {
            if(!dfs(i, 1))  // 若是頂點尚未染色,就染色成1
            {
                printf("No\n");
                return 0;
            }
        }
    }
    printf("Yes\n");
    return 0;
}

最小生成樹

Kruskal

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct Node {
    int a,b,val;
    friend bool operator < (const Node& x,const Node& y) {
        return x.val< y.val;  //對於sort重載的話,從小到大用小於號
    }
} load[1000];
int sum=0;
int n,m;
int fa[1000];
int cnt=0;
int Find(int a) {
    return fa[a]==a ? a : fa[a]=Find(fa[a]);
}
void init(int a,int b) {
    fa[Find(b)]=Find(a);
}
void kruskal() {
    for(int i=0; i<m; i++) {
        int x,y,z;
        x=load[i].a;
        y=load[i].b;
        z=load[i].val;
        if(Find(x)!=Find(y)) {
            init(x,y);
            cnt++;
            sum+=z;
        }
        if(cnt==n-1) {
            break;
        }
    }
}
int main() {
    cin>>n>>m;
    for(int i=0; i<m; i++) {
        cin>>load[i].a>>load[i].b>>load[i].val;
    }
    for(int i=1; i<=n; i++) {
        fa[i]=i;
    }
    sort(load,load+m);
    kruskal();
    cout<<sum<<endl;
    return 0;
}

Prim

#include<iostream>
#include<stdio.h>
#include<queue>
#include<vector>
#include<stack>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;
struct Node {
    int d,len;
    friend bool operator < (const Node& a,const Node& b) {
        return a.len > b.len; //對於優先隊列,從小到大排序用大於號
    }
};
int n,m;
int cnt,sum;
int vis[1000];
vector <Node> v[1000];
priority_queue< Node > q;
void prim() {
    vis[1]=1;
    for(int i=0; i<v[1].size(); i++) {
        q.push(v[1][i]);
    }
    while(!q.empty()) {
        Node now = q.top();
        q.pop();
        if(vis[now.d]) {
            continue;
        }
        vis[now.d]=1;
        cnt++;
        sum+=now.len;
        for(int i=0; i<v[now.d].size(); i++) {
            if(vis[v[now.d][i].d]) continue;
            q.push(v[now.d][i]);
        }
        if(cnt==n-1) break;
    }
}
int main() {
    while(cin>>m>>n) {
        if(m==0) break;
        cnt=sum=0;
        while(!q.empty()) {
            q.pop();
        }
        for(int i=0; i<=n; i++) {
            v[i].clear();
        }
        memset(vis,0,sizeof(vis));
        for(int i=0; i<m; i++) {
            int x,y,z;
            cin>>x>>y>>z;
            Node tmp;
            tmp.d=y;
            tmp.len=z;
            v[x].push_back(tmp);
            tmp.d=x;
            v[y].push_back(tmp);
        }
        prim();
        if(cnt==n-1) {
            cout<<sum<<endl;
        } else {
            cout<<'?'<<endl;
        }
    }
    return 0;
}

7、字符串匹配

有限自動機

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
bool Matching_Prefix_Suffix(char* P,int k,int q,char c)
{                               //P爲模式串 K爲要驗證的前綴和後綴的字符串長度
    if(k==0)                    //q爲當前自動機主線長度
    return true;              //k=0 空字符串 前綴和後綴確定相等
    if(k==1){                //只有一個字符串 證實自動機恰好開始建立
        return P[0]==c;      //若是模式串的第一個和其中的c相等 前綴等於後綴
    }
    return P[k-1]==c&& (!strncmp(P,P+q-k+1,k-1));    //檢驗P[0...k-1]==P[q-k+1]
}
 
vector<map<char,int> > Compute_Transition_Function( char *P,const char* input_character)
{                                //計算轉移函數的值
    int m=strlen(P);                //模式串的長度
    int j=0,k;
    printf("The main length of Finite_Automaton_Matcher is %d\n",m);
    vector<map<char,int> >transition_map(m+1);         //建立一個vector 一共有m+1個數據
    for(int i=0;i<m;i++){                   //對於模式串的長度
        j=0;
        while(input_character[j]!='\0'){      //對於輸入串的每一種可能字符
         k= min(m+1,i+2);               //由於對於長度爲i的字符串 它的轉移函數最大值爲i
        do{                          //數組下標從0開始 再加上後面k一來就減1  因此爲i+2
            k=k-1;                  //找到一個最大值k使得模式串的P[0...k]==P[...n-1]
    }while(!Matching_Prefix_Suffix(P,k,i,input_character[j]));
        transition_map[i][input_character[j]]=k;//δ(q,a)=k a爲輸入字母表中的字母,q爲狀態 
        j++;
        }
    }
    return  transition_map;           //返回一個vector 每個元素爲 map<char,int>
}                                     //char 爲自動機中的字符 int 爲轉移函數值
 
void Finite_Automaton_Matcher(char* T,char* P,vector<map<char,int> >transition_map)
{
    int n=strlen(T);                           //文本串長度
    int m=strlen(P);                           //模式串長度
    int q=0;                                   //轉移函數的值
     for(int i=0;i<n;i++){                    //對於文本串中的每個字符
     q = transition_map[q][T[i]];             //迭代 前一個字符的轉移函數值
     if(q==m)                                //轉移函數的值等於模式串的長度
     printf("Pattern occurs with shift %d\n",i+1-m);   //模式串的有效位移爲i-m+1
    }
}
int main()
{
    const char* input_character="abc";      //輸入字母表
    char T[]="abababacaba";                 //文本串
    char P[]="ababaca";                     //模式串
    vector<map<char,int> >transition_map=Compute_Transition_Function(P,input_character);
    Finite_Automaton_Matcher(T,P,transition_map);
    return 0;
}

優化後的自動機

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#define MAX 200
using namespace std;
int next[MAX];
void getnext(int next[],char* t) {
    int k=0;
    next[1]=0;
    int lent=strlen(t);
    for(int q=2;q<=lent;q++){
        while(k>0&&t[k+1]!=t[q]){
            k=next[k];
        }
        if(t[k+1]==t[q]) k=k+1;
        next[q]=k;
    }
}
bool Matching_Prefix_Suffix(char* P,int k,int q,char c) {
    //P爲模式串 K爲要驗證的前綴和後綴的字符串長度
    if(k==0)                    //q爲當前自動機主線長度
        return true;              //k=0 空字符串 前綴和後綴確定相等
    if(k==1) {               //只有一個字符串 證實自動機恰好開始建立
        return P[0]==c;      //若是模式串的第一個和其中的c相等 前綴等於後綴
    }
    return P[k-1]==c&& (!strncmp(P,P+q-k+1,k-1));    //檢驗P[0...k-1]==P[q-k+1]
}

vector<map<char,int> > Compute_Transition_Function( char *P,const char* input_character) {
    //計算轉移函數的值
    int m=strlen(P);                //模式串的長度
    int j=0,k;
    getnext(next,P);
    printf("The main length of Finite_Automaton_Matcher is %d\n",m);
    vector<map<char,int> >transition_map(m+1);//建立一個vector 一共有m+1個數據
    for(int i=0; i<=m; i++) {                //對於模式串的長度
        j=0;
        while(input_character[j]!='\0') {     //對於輸入串的每一種可能字符
            if(P[i+1]==input_character[j]) transition_map[i][input_character[j]]=i+1;
            else if(P[i+1]!=input_character[j]||i==m) 
                transition_map[i][input_character[j]]=transition_map[next[i]][input_character[j]];
            j++;
        }
    }
    return  transition_map;           //返回一個vector 每個元素爲 map<char,int>
}                                     //char 爲自動機中的字符 int 爲轉移函數值
void Finite_Automaton_Matcher(char* T,char* P,vector<map<char,int> >transition_map) {
    int n=strlen(T);                           //文本串長度
    int m=strlen(P);                           //模式串長度
    int q=0;                                   //轉移函數的值
    for(int i=0; i<n; i++) {                 //對於文本串中的每個字符
        q = transition_map[q][T[i]];             //迭代 前一個字符的轉移函數值
        if(q==m-1)                                //轉移函數的值等於模式串的長度
            printf("Pattern occurs with shift %d\n",i+1-m);//模式串的有效位移爲i-m+1
    }
}
int main() {
    const char* input_character="abc";      //輸入字母表
    char T[MAX],P[MAX];
    scanf("%s%s",T,P);
    //char T[]="abababacaba";                 //文本串
    //char P[]="ababaca";                     //模式串
    vector<map<char,int> >transition_map=Compute_Transition_Function(P,input_character);
    Finite_Automaton_Matcher(T,P,transition_map);
    return 0;
}

KMP算法

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 200
using namespace std;
int next[MAX];
void getnext(int next[],char* t) {
    int j=0,k=-1;
    next[0]=-1;
    int lent=strlen(t);
    while(j<lent) {
        if(k == -1 || t[j] == t[k]) {
            j++;
            k++;
            next[j]=k;
        } 
        else k = next[k];
    }
}
int KMP(char* s,char* t) {
    int i=0,j=0;
    getnext(next,t);
    int lens,lent;
    lens=strlen(s),lent=strlen(t);
    while(i<lens&&j<lent) {
        if(j==-1 || s[i]==t[j]) {
            i++;
            j++;
        } 
        else j=next[j];             //j回退。。。
    }
    if(j>=lent)
        return i-j;         //匹配成功,返回子串的位置
    else
        return 0;                 //沒找到
}
int main(){
    char s[MAX],t[MAX];
    scanf("%s%s",s,t);
    printf("%d",KMP(s,t));
}
//計算子串出現次數,直接使用便可
int KMPcount(char *s,char *t){///計算模式串在子串出現的次數
    getnext(next,t);
    int i=0,j=0;
    int lens=strlen(s),lent=strlen(t);
    int ans=0;
    while(i<lens){
        while(j!=-1&&t[j]!=s[i]) 
            j=next[j];
        i++,j++;
        if(j==lent) 
            ans++;
    }
    return ans;///返回模式串在主串中出現的次數(可重疊出現)
}

擴展KMP

定義母串S,和字串T,設S的長度爲n,T的長度爲m,求T與S的每個後綴的最長公共前綴,也就是說,設extend數組,extend[i]表示T與S[i,n-1]的最長公共前綴,要求出全部extend[i](0<=i<n)

#include <iostream>
#include <string>
#include<string.h>
#define MAX 200
using namespace std;
/* 求解 T 中 next[],註釋參考 GetExtend() */
void GetNext(char* T, int &m, int next[]){
    int a=0,p=0;
    next[0]=m;
    for (int i=1;i<m;i++){
        if (i>=p||i+next[i-a]>=p){
            if(i>=p)
                p=i;
            while(p<m&&T[p]==T[p-i])
                p++;
            next[i]=p-i;
            a=i;
        }
        else
            next[i]=next[i-a];
    }
}
/* 求解 extend[] */
void GetExtend(char *S,int& n,char* T,int& m,int extend[],int next[]){
    int a=0,p=0;
    GetNext(T,m,next);
    for (int i=0;i<n;i++){
        if (i>=p||i+next[i-a]>=p){ // i >= p 的做用:舉個典型例子,S 和 T 無一字符相同
            if(i>=p)
                p=i;
            while(p<n&&p-i<m&&S[p]==T[p-i])
                p++;
            extend[i]=p-i;
            a=i;
        }
        else
            extend[i]=next[i-a];
    }
}
int main(){
    int next[100];
    int extend[100];
    //string S, T;
    char S[MAX],T[MAX];
    int n,m;
    while (~scanf("%s%s",S,T)){
        n=strlen(S);
        m=strlen(T);
        GetExtend(S,n,T,m,extend,next);
        //打印 next
        printf("next:   ");
        for (int i=0;i<m;i++)
            printf("%d ",next[i]);
        //打印 extend
        for (int i=0;i<n;i++)
            printf("%d ",extend[i]);
        puts("");
    }
    return 0;
}

KMP例題

/*字符串是循環節循環構成的(循環節能夠是其自己)
輸出該循環節出現的次數*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define Memset(x, a) memset(x, a, sizeof(x))
using namespace std;
const int N=1e6+10;
int next[N];
char s[N];
void  getNext(const char P[],int next[]) {
    int  m=strlen(P);
    int i=0,j;
    j=next[0]=-1;
    while(i<m) {
        while(-1!=j && P[i]!=P[j])j=next[j];
        next[++i]=++j;
    }
}
int main() {
    while(~scanf("%s",s)) {
        if(s[0]=='.') break;
        Memset(next,0);
        getNext(s,next);
        int  len=strlen(s);
        if(len%(len-next[len])==0) printf("%d\n",len/(len-next[len]));
        else printf("1\n");
    }
    return 0;
}
/*與上文相似只是有些微區別
給你一個字符串,求這個字符串到第i個字符爲止的循環節的次數。
前提是存在循環,若是不存在,則不輸出,例如:
in:abab out: 4 2
in:a out:無
in:abcabcd out: 6 2
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define Memset(x, a) memset(x, a, sizeof(x))
using  namespace std;
const int N=1e6+10;
char s[N];
int next[N];
int n;
void  getNext(const char P[],int next[]) {
    int  m=strlen(P);
    int i=0,j;
    j=next[0]=-1;
    while(i<m) {
        while(-1!=j && P[i]!=P[j])j=next[j];
        next[++i]=++j;
    }
}
int main() {
    int kase=0;
    while(~scanf("%d",&n)&&n) {
        scanf("%s",s);
        Memset(next,0);
        getNext(s,next);
        printf("Test case #%d\n",++kase);
        for(int i=2; i<=n; i++) {
            if(next[i]>0&&i%(i-next[i])==0)printf("%d %d\n",i,i/(i-next[i]));
        }
        printf("\n");
    }
    return 0;
}
/*給定S,求出S的全部可能的相同先後綴的長度
next[len-1]爲最長的相同先後綴並設爲S1,而後S1的最長的
相同先後綴設爲S2,表示爲next[S1.size()-1],確定也是S的
相同先後綴,這樣循環便可求出全部,別忘記其自己*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define Memset(x, a) memset(x, a, sizeof(x))
using namespace std;
const int N=4e5+10;
int next[N],ans[N];
char s[N];
void  getNext(const char P[],int next[]) {
    int  m=strlen(P);
    int i=0,j;
    j=next[0]=-1;
    while(i<m) {
        while(-1!=j && P[i]!=P[j])j=next[j];
        next[++i]=++j;
    }
}
int main() {
    while(~scanf("%s",s)) {
        Memset(next,0);
        getNext(s,next);
        int cnt=0;
        int len=strlen(s);
        int j=next[len];
        while(j>0) {
            ans[++cnt]=j;
            j=next[j];
        }
        for(int i=cnt; i>0; i--)printf("%d ",ans[i]);
        printf("%d\n",len);
    }
    return 0;
}
/*1題目要求的是給定一個字符串,問咱們還須要添加幾個字符能夠構成一個由n個循環節組成的字符串。
2可知咱們應該先求出字符串的最小循環節的長度:假設字符串的長度爲len,那麼最小的循環節就是cir = len-next[len];若是有len%cir == 0,那麼這個字符串就是已是完美的字符串,不用添加任何字符;若是不是完美的那麼須要添加的字符數就是cir - (len-(len/cir)*cir)),至關與須要在最後一個循環節上面添加幾個。
3若是cir = 1,說明字符串只有一種字符例如「aaa」 ; 若是cir = m說明最小的循環節長度爲m,那麼至少還需m個;若是m%cir == 0,說明已經不用添加了*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100010
char s[N];
int nextval[N];
int len;
void getnext(const char *s){
    int i = 0, j = -1;
    nextval[0] = -1;
    while(i != len){
        if(j == -1 || s[i] == s[j])
            nextval[++i] = ++j;
        else
            j = nextval[j];
    }
}
int main(){
    int ncase;
    int length, add;
    scanf("%d", &ncase);
    while(ncase--){
        scanf("%s", s);
        len = strlen(s);
        getnext(s);
        /*for(int i = 0; i <= len; ++i) //查看next數組的內容
            cout<<nextval[i]<<" ";
        cout<<endl;*/
        length = len - nextval[len]; //循環節的長度
        cout << length <<endl;
        if(len != length && len % length == 0) //循環屢次
            printf("0\n");
        else{
            add = length - nextval[len] % length; //取餘的做用:abcab,去掉abc
            printf("%d\n",add);
        }
    }
    return 0;
}
/*難題
給定一個大矩陣,求出求最小覆蓋矩陣,大矩陣可由這個小矩陣拼成。
容許最後有超出的地方,如:abababa,ab可覆蓋,abab也可
輸出該矩陣的面積*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxr=10002;
const int maxc=80;
char grid[maxr][maxc]; //大矩陣
int row,col;//行和列
int rnext[maxr][maxc]; //rnext[i]:對應第i行字符串的next函數
int cnext[maxr]; //求縱向的next,每次比較的是整行
int rlen[maxr]; //rlen[i]:第i行字符串的最小循環子串的長度
int cnt[maxc];//cnt[i]:統計各寬度出現的次數
int ans_c,ans_r; //最小覆蓋矩陣的寬度和高度
void rgetNext(int r,char*str){
    int k=0;
    rnext[r][1]=0;
    for(int i=1;i<col;i++){
        while(k&&str[k]!=str[i])
            k=rnext[r][k];
        if(str[k]==str[i])
            k++;
        rnext[r][i+1]=k;
    }
    rlen[r]=col-rnext[r][col];
    int i;
    for(i=rlen[r];i<=col;i+=rlen[r]){
        cnt[i]++;
    }
    i-=rlen[r];
    //直接經過比較來判斷,是否還有可能存在的串,如aaabcaaa,除了5,還可能爲6,7,8
    //即判斷第i+1個字符後的後綴是否和前綴相同
    for(int j=i+1;j<=col;j++){
        int x=0,y=j;//分別從索引0和y處開始比較
        while(str[x]==str[y]){
            x++;y++;
        }
        if(y==col)
            cnt[j]++;
    }
}
void cgetNext(){
    int k=0;
    cnext[1]=0;
    for(int i=1;i<row;i++){
        while(k&&strcmp(grid[k],grid[i])!=0)
            k=cnext[k];
        if(strcmp(grid[k],grid[i])==0)
            k++;
        cnext[i+1]=k;
    }
    ans_r=row-cnext[row];
}
int main(){
    scanf("%d%d",&row,&col);
    for(int i=0;i<row;i++)
        scanf("%s",grid[i]);
    memset(cnt,0,sizeof(cnt));
    for(int i=0;i<row;i++)
        rgetNext(i,grid[i]);
    cgetNext();
    for(int i=1;i<=col;i++){
        if(cnt[i]==row){
            ans_c=i;
            break;
        }
    }
    printf("%d\n",ans_c*ans_r);
    return 0;
}
/*最短公共祖先問題
給定兩個串,用其組成一個新串使得新串包含這兩串
求該新串的最短長度*/
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstring> 
using namespace std; 
const int N = 1000100;
char a[3][2*N];
int fail[2*N];
inline int max(int a, int b){
    return (a>b)?a:b;
}
int kmp(int &i,int &j,char* str,char* pat){
    int k;
    memset(fail,-1,sizeof(fail));
    for (i=1;pat[i];++i){
        for(k=fail[i-1];k>=0&&pat[i]!=pat[k+1];k=fail[k]);
        if(pat[k+1]==pat[i]){
            fail[i]=k+1;
        }
    }
    i=j=0;
    while(str[i]&&pat[j]){
        if (pat[j]==str[i]){
            i++;
            j++;
        }
        else if(j==0)
            i++;    
        else j=fail[j-1]+1;
    }
    if(pat[j]) return -1;
    else return i-j;
}
int main(int argc,const char* argv[]){
    int T;
    scanf("%d",&T);
    while (T--){
        int i,j,l1=0,l2=0;
        scanf("%s%s",a[0],a[1]);
        //cin >> a[0] >> a[1];
        int len1=(int)strlen(a[0]),len2=(int)strlen(a[1]),val;
        val=kmp(i,j,a[1],a[0]);            
        if(val!=-1)
            l1=len1;
        else{
            if(i==len2&&j-1>=0&&a[1][len2-1]==a[0][j-1])
                l1=j;
        }
        val=kmp(i,j,a[0],a[1]);           
        if(val!=-1)
            l2=len2;
        else{
            if(i==len1&&j-1>=0&&a[0][len1-1]==a[1][j-1])
                l2=j;
        }
        printf("%d\n",len1+len2-max(l1, l2));
    }
    return 0;
}

8、FFT

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define N 150010
const double pi = 3.141592653;
char s1[N>>1], s2[N>>1];
double rea[N], ina[N], reb[N], inb[N];
int ans[N>>1];
void Swap(double *x, double *y)
{
    double t = *x;
    *x = *y;
    *y = t;
}
int Rev(int x, int len)
{
    int ans = 0;
    int i;
    for(i = 0; i < len; i++){
        ans<<=1;
        ans |= (x & 1);
        x>>=1;
    }
    return ans;
}//二進制的反轉x->ans
//做用就是把這n個數分紅咱們想要的lgn個部分,且每一個部分僅有兩個待處理的,而後再處理
//不太明白的能夠看看網上關於FFT中的二進制的翻轉問題的博客啥的
void FFT(double *reA, double *inA, int n, bool flag)
{
    int s;
    double lgn = log((double)n) / log((double)2);//定義log(2)(n),也就是表明分裂次數
    int i;
    for(i = 0; i < n; i++){
        int j = Rev(i, lgn);
        if(j > i){
            Swap(&reA[i], &reA[j]);
            Swap(&inA[i], &inA[j]);
        }
    }
    for(s = 1; s <= lgn; s++){//共進行lgn次
        int m = (1<<s);
        double reWm = cos(2*pi/m), inWm = sin(2*pi/m);//本原根
        if(flag) inWm = -inWm;//對C來講就要轉換爲負的
        int k;
        for(k = 0; k < n; k += m){
            double reW = 1.0, inW = 0.0;
            int j;
            for(j = 0; j < m / 2; j++){
                int tag = k+j+m/2;//能夠對照上文的公式看
                double reT = reW * reA[tag] - inW * inA[tag];
                double inT = reW * inA[tag] + inW * reA[tag];
                double reU = reA[k+j], inU = inA[k+j];
                reA[k+j] = reU + reT;
                inA[k+j] = inU + inT;
                reA[tag] = reU - reT;
                inA[tag] = inU - inT;
                double rew_t = reW * reWm - inW * inWm; 
                double inw_t = reW * inWm + inW * reWm; //這裏實現迭代
                reW = rew_t;
                inW = inw_t;
            }
        }
    }
    if(flag){//對C來講須要除以n
        for(i = 0; i < n; i++){
            reA[i] /= n;
            inA[i] /= n;
        }
    }
} 
int main(){
    while(~scanf("%s%s", s1, s2)){ 
        int flag=0;
        memset(ans, 0 , sizeof(ans));
        memset(rea, 0 , sizeof(rea));
        memset(ina, 0 , sizeof(ina));
        memset(reb, 0 , sizeof(reb));
        memset(inb, 0 , sizeof(inb));//初始化
        int i, lent, len = 1, len1, len2;
        len1 = strlen(s1);
        len2 = strlen(s2);
        /*if(s1[0]=='-'){
            for(int i=0;i<len1;i++){
                s1[i]=s1[i+1];
            }
            len1--;
            flag^=1;
        }
        if(s2[0]=='-'){
            for(int i=0;i<len2;i++){
                s2[i]=s2[i+1];
            }
            len2--;
            flag^=1;
        }*///符號的判斷
        lent = (len1 > len2 ? len1 : len2);
        while(len < lent) len <<= 1;
        len <<= 1;// 保證長度爲2的冪次,才能逐漸二分
        for(i = 0; i < len; i++){
            if(i < len1) rea[i] = (double)s1[len1-i-1] - '0';//將數組s1反轉,並保存爲double
            if(i < len2) reb[i] = (double)s2[len2-i-1] - '0';//將數組s2反轉,並保存爲double
            ina[i] = inb[i] = 0.0;
        }
        FFT(rea, ina, len, 0);//對A進行FFT
        FFT(reb, inb, len, 0);//對B進行FFT
        for(i = 0; i < len; i++){
            double rec = rea[i] * reb[i] - ina[i] * inb[i];
            double inc = rea[i] * inb[i] + ina[i] * reb[i];
            rea[i] = rec; ina[i] = inc;
        }//得到C的點值表達
        FFT(rea, ina, len, 1);
        for(i = 0; i < len; i++)
            ans[i] = (int)(rea[i] + 0.4);//舍入
        /*for(i = 0; i < len; i++){
            ans[i+1] += ans[i] / 10;
            ans[i] %= 10;
        }*///消除進位
        int len_ans = len1 + len2 + 2;
        while(ans[len_ans] == 0 && len_ans > 0) len_ans--;
        //if(flag) printf("-");
        for(i = len_ans; i >= 0; i--)
            printf("%d", ans[i]);
        printf("\n");
    }
    return 0;
}

9、常見技巧

關閉輸入輸出流

inline void init_cin() {     
    ios::sync_with_stdio(false);     
    cin.tie(nullptr); 
}

快速冪

long long qpow2(long long a, long long b){
    long long ans=1;
    if(b==0){
        return 1;
    } 
    if(b==1){
        return a%M;
    }
    while(b>0){
        if(b&1){
            ans=(ans%M)*(a%M)%M;
        }
        a=(a%M)*(a%M)%M;
        b>>=1; 
    } 
    return ans%M;
}

差分

B[i]=A[i]-A[i-1];
//要使A[l,r]每一個數加上一個d,能夠轉換爲操做:B[l]+d,B[r+1]-d
相關文章
相關標籤/搜索