補充:需要掌握的演算法
阿新 • • 發佈:2019-02-13
01揹包和完全揹包
使用一維陣列的版本:
01揹包:
int main() {
int n,m;
while(cin>>n>>m) {
vector<int> weight(n+1,0);//物品的重量
vector<int> value(n+1,0);//物品的價值
vector<int> dp(m+1,0);//一維陣列,存放用n個產品裝容量為i的揹包的解
for(int i=0;i<n;i++)
cin>>weight[i+1 ]>>value[i+1];//輸入
for(int i=1;i<=n;i++)
//這裡是逆序
//用j>=weight[i]作為判斷條件,省略了一個if語句
for(int j=m;j>=weight[i];j--) {
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
cout<<dp[m]<<endl;
}
return 0;
}
完全揹包:
只需要將上面的逆序改為順序
for(int i=1;i<=n;i++)
//這裡變成了順序
//這裡設j從weight[i]開始,省略了一個if語句
for(int j=weight[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
O(nlogn)的排序
快排(單向掃描版)
int partition(int a[], int l, int r) {
int x = a[l];//選擇a[l]作為選定元素
int i = l;
int j = l+1;
for (; j <= r; j++)
if (a[j] < x) //如果a[j]小於x,就將它交換到前面去
swap(&a[++i], &a[j]);
swap(&a[l], &a[i]);//最終將a[l]交換到正確的位置上
return i;
}
void quick(int a[],int l,int r) {
if (l>=r) return;//如果子區間為0或1個元素,就可以結束遞迴呼叫
int i = partition(a,l,r);
quick(a,l,i-1);
quick(a,i+1,r);
}
歸併排序
int b[maxN]; //輔助陣列
void partition(int a[],int l,int r) {
int i,j,k;
int m = (l+r)/2;
//將排好序了的兩個子陣列:a[1~m]和a[m+1~r]放入輔助陣列b中
for (i=l;i<=r;i++) b[i]=a[i];
//i和j分別指向兩個子陣列的開頭
i=l;
j=m+1;
k=l;
while(i<=m && j<=r) {
if (b[i]<b[j])
a[k++]=b[i++];
else
a[k++]=b[j++];
}
//如果後半個數組已經全部處理完了
while(i<=m)
a[k++]=b[i++];
//如果前半個陣列已經全部處理完了
while(j<=r)
a[k++]=b[j++];
}
void merge(int a[],int l,int r) {
//當子序列只有1個元素或為空時,返回
if (l>=r) return;
int m = (l+r)/2;
merge(a,l,m);
merge(a,m+1,r);
partition(a,l,r);
}
堆排序
堆本質上是完全二叉樹,用陣列(vector)表示。首先定義一個數組,用來表示堆。
int N=0;
vector<int> heap(100005,0);
上浮和插入操作:
void Up(int k) {
while(k>1 && heap[k/2]<heap[k]) {//是否到根節點了,或者父節點比當前節點小
swap(heap[k/2],heap[k]);
k=k/2;
}
}
void insert(int weight) {
N++;
heap[N]=weight;
Up(N);
}
下沉和刪除操作:
void Down(int k) {
int j;
while(2*k<=N) {//如果沒到達樹底
j=2*k;
if(j<N && heap[j+1]>heap[j])
j++;//找到子節點中較大的那個
if(heap[k]>heap[j])
break;//如果父節點比子節點都大,退出
swap(heap[k],heap[j]);
k=j;
}
}
int deleteMax() {
swap(heap[N],heap[1]);
N--;
Down(1);
return heap[N+1];
}
堆排序:
void PQsort(int a[],int l,int r) {
int k;
for (k=l;k<=r;k++) insert(a[k]);
for (k=r;k>=l;k--) a[k]=deleteMax();
}
迪傑斯特拉演算法
int main() {
int n,m,s,t;//分別是節點數、邊的條數、起點、終點
while(cin>>n>>m>>s>>t) {
vector<vector<int>> edge(n+1,vector<int>(n+1,0));//鄰接矩陣
vector<int> dis(n+1,0);//從起點出發的最短路徑
vector<int> book(n+1,0);//某結點已經被訪問過
for(int i=1;i<=n;i++)//初始化鄰接矩陣
for(int j=1;j<=n;j++)
if(i!=j) edge[i][j]=INT_MAX;
int u,v,length;
for(int i=0;i<m;i++) {//讀入每條邊,完善鄰接矩陣
cin>>u>>v>>length;
if(length<edge[u][v]) {//如果當前的邊長比已有的短,則更新鄰接矩陣
edge[u][v]=length;
edge[v][u]=length;
}
}
for(int i=1;i<=n;i++)//初始化dis陣列
dis[i]=edge[s][i];
book[s]=1;//把起點先標記一下
//演算法核心!!!先確定沒訪問過的節點中,離起點最近的,然後鬆弛
for(int i=1;i<=n;i++)
{
int min=INT_MAX;
int index=0;
for(int j=1;j<=n;j++) {
{
min=dis[j];
index=j;
}
}
book[index]=1;//標記這個剛剛被確定了的節點
if(index==t) break;//如果已經到終點了,就直接結束
for(int i=1;i<=n;i++)
{//從剛被確定的節點出發,鬆弛剩下的節點
if(book[i]==0 && edge[index][i]<INT_MAX && dis[i] > dis[index]+edge[index][i])
dis[i] = dis[index]+edge[index][i];
}
}
cout<<dis[t]<<endl;
}
return 0;
}
並查集
#include<iostream>
using namespace std;
int pre[1050]; //儲存節點的直接父節點
//查詢x的根節點
int find(int a){
if(pre[a]!=a)
pre[a]=find(pre[a]);//路徑壓縮,本結點更新為根結點的子結點
return pre[a];
}
//連線兩個連通塊
void join(int x,int y) {
int fx=Find(x),fy=Find(y);
if(fx!=fy) pre[fy]=fx;
}
int main() {
int N,M,a,b,i,j,ans=0;
while(scanf("%d%d",&N,&M) && N) {
//初始化pre陣列
for(i=1;i<=N;i++) pre[i]=i;
//根據連通情況,構建pre陣列
for(i=1;i<=M;i++) {
scanf("%d%d",&a,&b);
join(a,b);
}
for(i=1;i<=N;i++)
if(pre[i]==i) ans++; //計算連通子圖的個數ans
cout<<ans;
return 0;
}
拓撲排序
若用鄰接表表示圖的資訊,則有:
int in[100001]={0};//記錄每個節點的入度
vector<int> edge[100001]; //鄰接表
int main() {
int n,m;//節點數和順序關係的數量
while(cin>>n>>m) {
int from,to;
int i,k;
int index;
int cnt=0;
for(i=1;i<=m;i++) {//初始化每個節點的入度
cin>>from>>to;
edge[from].push_back(to);
in[to]++;
}
for(k=1;k<=n;k++)
{
index=0;//當前這輪確定了的節點編號
bool valid = false;
for(i=1;i<=n;i++) {
if(in[i]==0) {//若某個未確定節點的入度為0
index=i;
cnt++;
in[i]--;
valid = true;
break;
}
}
if(!valid) {//如果在未確定的節點中,找不到入度為0的
cout<<"Wrong"<<endl;
break;
}
for(auto p : edge[index]) {//刪除從節點index出發的邊,將終點的入度減一
in[p]--;
}
}
if(cnt==n) cout<<"Correct"<<endl;
}
return 0;
}
KMP演算法
本質上,是利用匹配子串的最長公共前後綴的資訊來提高效率。
void makeNext(string P,vector<int> &next)
{
int q,k;
int m = P.size();
next[0] = 0;
for (q = 1, k = 0;q < m;q++)
{
while(k > 0 && P[q] != P[k])
k = next[k-1];
if (P[q] == P[k])
k++;
next[q] = k;
}
}
int kmp(string T,string P,vector<int> &next) {
int n=T.size();
int m=P.size();
int i=0,q=0;
int ans=0;
for (;i < n;i++) {
while(q > 0 && P[q] != T[i])
//如果失配了,退回q-(q-next[q-1]) = next[q-1]位
q = next[q-1];
if (P[q] == T[i]) //如果當前字元匹配成功,q後移一位
q++;
if (q == m) //如果已經成功匹配一次
ans++;
}
return ans;
}
int main() {
int N;
string mo,str;
cin>>N;
while(N--) {
cin>>mo>>str;
vector<int> next(mo.size(),0);
makeNext(mo,next);//構建部分匹配表
cout<<kmp(str,mo,next)<<endl;//輸出總的匹配個數
}
return 0;
}