所有實驗

實驗一

實驗?zāi)康呐c要求:理解分治法的基本思想和設(shè)計方法。

實驗題目:

1.實現(xiàn)基于分治法的歸并排序算法.

#include <iostream>
using namespace std;

void merge(int *data, int p, int q, int r)
{
    int n1, n2, i, j, k;  
    int *left=NULL, *right=NULL;  
    n1 =q-p+1;   
    n2 =r-q;  
    
    left = (int *)malloc(sizeof(int)*(n1));   
    right = (int *)malloc(sizeof(int)*(n2));  
    for(i=0; i<n1; i++)  //對左數(shù)組賦值  
        left[i] = data[p+i];  
    for(j=0; j<n2; j++)  //對右數(shù)組賦值  
        right[j] = data[q+1+j];  
    i =j=0;   
    k=p;  
    while(i<n1&&j<n2) //將數(shù)組元素值兩兩比較,并合并到data數(shù)組  
    {  
        if(left[i]<=right[j])  
            data[k++]=left[i++];  
        else  
            data[k++] = right[j++];  
    }  
    for(;i<n1;i++) //如果左數(shù)組有元素剩余,則將剩余元素合并到data數(shù)組  
        data[k++] = left[i];  
    for(;j<n2;j++) //如果右數(shù)組有元素剩余,則將剩余元素合并到data數(shù)組  
        data[k++] = right[j];  
}


void merge_Sort(int *data, int p, int r)
{
    int q;  
    if(p<r) //只有一個或無記錄時不須排序   
    {  
        q = (int)((p+r)/2);      //將data數(shù)組分成兩半     
        merge_Sort(data, p, q);   //遞歸拆分左數(shù)組  
        merge_Sort(data, q+1, r); //遞歸拆分右數(shù)組  
        merge(data, p, q, r);    //合并數(shù)組  
    }  
    
}

void merge_sort(int a[],const int size)
{
    merge_Sort(a,0,size-1);
}



void main()
{
    int count=7;
    int a[]={1,3,2,4,5,1,2};
    merge_sort(a,count); //排序
    for(int i=0;i<count;i++)
        cout<<a[i]<<" ";
}

2.實現(xiàn)快速排序的算法,并嘗試采用不同的方法實現(xiàn)線性的劃分過程.

#include <iostream>
using namespace std;

void quick_sort(int a[], int left, int right)
{
    if(left<right)  
    {  
        int i = left;  
        int j = right;  
        int x = a[i];  
        
        while(i<j)  
        {  
            while(i<j&&a[j]>x)  
                j--;  
            if(i<j){  
                a[i] = a[j];  
                i++;  
            }  
            while(i<j&&a[i]<x)  
                i++;  
            if(i<j){  
                a[j] = a[i];  
                j--;  
            }  
        }  
        a[i] = x;  
        quick_sort(a, left, i-1);  
        quick_sort(a, i+1, right);  
    } 
}
void main()
{
    int a[]={1,2,3,4,5,8,6,1};
    int size=8;
    quick_sort(a,0,size-1);
    for(int i=0;i<size;i++)
        cout<<a[i]<<" ";
}

3.有一個數(shù)的序列A[1]、A[2] 、A[3] 、…… 、A[n],若i<j,并且A[i]>A[j],則稱A[i]與A[j]構(gòu)成了一個逆序?qū)?,設(shè)計算法求數(shù)列A中逆序?qū)Φ膫€數(shù).

#include <iostream>
using namespace std;

int merge(int *data, int p, int q, int r)
{
    int n1, n2, i, j, k;  
    int *left=NULL, *right=NULL;  
    int count=0;
    n1 =q-p+1;   
    n2 =r-q;  
    
    left = (int *)malloc(sizeof(int)*(n1));   
    right = (int *)malloc(sizeof(int)*(n2));  
    for(i=0; i<n1; i++)  //對左數(shù)組賦值  
        left[i] = data[p+i];  
    for(j=0; j<n2; j++)  //對右數(shù)組賦值  
        right[j] = data[q+1+j];  
    i =j=0;   
    k=p;  
    while(i<n1&&j<n2) //將數(shù)組元素值兩兩比較,并合并到data數(shù)組  
    {  
        if(left[i]<=right[j])  
            data[k++]=left[i++]; 
            
        else  
        {
            count=count+n1-i;
            data[k++] = right[j++];  
        }
    }  
    for(;i<n1;i++) //如果左數(shù)組有元素剩余,則將剩余元素合并到data數(shù)組  
    {
        data[k++] = left[i];    
    }
    for(;j<n2;j++) //如果右數(shù)組有元素剩余,則將剩余元素合并到data數(shù)組  
        data[k++] = right[j];  
    return count;
}

int merge_Sort(int *data, int p, int r)
{
    int count1,count2,count3,count=0;
    int q;  
    if(p<r) //只有一個或無記錄時不須排序   
    {  
        
        q = (int)((p+r)/2);      //將data數(shù)組分成兩半     
        count1=merge_Sort(data, p, q);   //遞歸拆分左數(shù)組  
        count2=merge_Sort(data, q+1, r); //遞歸拆分右數(shù)組
        count3=merge(data, p, q, r);    //合并數(shù)組
        count=count1+count2+count3;
    }  
    return count;
}

int merge_sort(int a[],const int size)
{
    int count = merge_Sort(a,0,size-1);
    return count;
}



void main()
{
    int size=7;
    int a[]={1,3,7,8,5,1,7};
    int count=merge_sort(a,size); //排序
    for(int i=0;i<size;i++)
        cout<<a[i]<<" ";
    cout<<endl<<"逆序?qū)?shù):"<<count<<endl;
}

4.(選做題) 引入逆序計數(shù)問題作為考察兩個序列有多大差別的一個好的度量指標。但是人們可能感覺這個量度太敏感了。如果i<j,并且A[i]>2A[j],我們把這對i,j叫做重要的逆序。設(shè)計一個O(nlogn) 的算法計數(shù)在兩個序列中的重要逆序個數(shù)。

實驗二

實驗?zāi)康呐c要求:理解分治法的基本思想和設(shè)計方法。

實驗題目:

1.k-路合并操作問題:假定有k個有序數(shù)組,每個數(shù)組中含有n個元素,您的任務(wù)是將它們合并為單獨的一個有序數(shù)組,該數(shù)組共有kn個元素。設(shè)計和實現(xiàn) 一個有效的分治算法解決k-路合并操作問題,并分析時間復(fù)雜度。

#include <iostream>  
using namespace std;  
  
#define LEN 10          //最大歸并段長  
#define MINKEY -1     //默認全為正數(shù)  
#define MAXKEY 100    //最大值,當(dāng)一個段全部輸出后的賦值  
  
struct Array  
{  
    int arr[LEN];  
    int num;  
    int pos;  
}*A;  
  
    int k,count;  
    int *LoserTree,*External;  
  
void Adjust(int s)  
{  
    int t=(s+k)/2;  
    int temp;  
    while(t>0)  
    {  
        if(External[s] > External[LoserTree[t]])  
        {  
            temp = s;  
            s = LoserTree[t];  
            LoserTree[t]=temp;  
        }  
        t=t/2;  
    }  
    LoserTree[0]=s;  
}  
  
void CreateLoserTree()  
{  
    External[k]=MINKEY;  
    int i;  
    for(i=0;i<k;i++)LoserTree[i]=k;  
    for(i=k-1;i>=0;i--)Adjust(i);  
}  
  
void K_Merge()  
{  
    int i,p;  
    for(i=0;i<k;i++)  
    {  
        p = A[i].pos;  
        External[i]=A[i].arr[p];  
        //cout<<External[i]<<",";  
        A[i].pos++;  
    }  
    CreateLoserTree();  
    int NO = 0;  
    while(NO<count)  
    {  
        p=LoserTree[0];  
        cout<<External[p]<<",";  
        NO++;  
        if(A[p].pos>=A[p].num)External[p]=MAXKEY;  
        else   
        {  
            External[p]=A[p].arr[A[p].pos];  
            A[p].pos++;  
        }  
        Adjust(p);  
    }  
    cout<<endl;  
}  
  
int main()  
{  
    freopen("in.txt","r",stdin);  
  
    int i,j;  
    count=0; 
    cin>>k;  
    A=(Array *)malloc(sizeof(Array)*k);  
    for(i=0;i<k;i++)  
    {  
        cin>>A[i].num;  
        count=count+A[i].num;  
        for(j=0;j<A[i].num;j++)  
        {  
            cin>>A[i].arr[j];  
        }  
        A[i].pos=0;  
    }  
    LoserTree=(int *)malloc(sizeof(int)*k);  
    External=(int *)malloc(sizeof(int)*(k+1));  
  
    K_Merge();  
  
    return 0;  
}  

附件-文件in.txt

5
1 5 6 8 25
6
2 6 9 25 30 32
3
5 9 16
6
6 9 15 24 30 36
2
8 34

2.Split操作的要求是:以一個數(shù)組s和一個值v為輸入,將數(shù)組s劃分成3個子集:比v小的元素組成的集合,等于v的元素組成的集合以及比v大的元素組成的集合。設(shè)計和實現(xiàn)一種O(n)的就地split算法,即該算法不額外分配新的內(nèi)存。

#include <iostream>
#include <VECTOR>
using namespace std;

void Split(std::vector< int > &array,const int value)
{
    int length=array.size();
    int i=0,j=length-1;
    while(i<j)
    {
        while(array[i]<=value)
            ++i;
        if((i<j))
            std::swap(array[i],array[j--]);
    }
    int max_pot=i;
    i=0;
    cout<<endl<<max_pot<<endl;
    for(int i=0;i < array.size();++i)
        cout<<array[i]<<" ";
    while(i<j)
    {
        while(array[i]<value)
            ++i;
        while(array[j]>=value)
            --j;
        if((array[i]==value)&&(i<j))
            std::swap(array[i++],array[j--]);
    }
    int min_pot=i;
}
int main()
{
    int a[20]={1,3,4,2,5,5,5,7,9,19,4,6,5,7,23,3,4,5,8,12};
    vector<int> array(a,a+20);
    cout<<"Testing Data:\n";
    for(int i=0;i < array.size();++i)
        cout<<array[i]<<" ";
    Split(array,5);
    cout<<"\nOutput:\n";
    for(int i=0;i < array.size();++i)
        cout<<array[i]<<" ";
    return 0;
}

實驗三

實驗?zāi)康呐c要求:理解分治法的基本思想和設(shè)計方法。

實驗題目:

1.尋找中項
【問題描述】
對于長度為n的整型數(shù)組A,隨機生成其數(shù)組元素值,然后實現(xiàn)一個線性時間的算法,在該數(shù)組中查找其中項。


#include<iostream>
#include<stdio.h>
#include <vector>
#include <time.h>
#include <stdlib.h>

int searchMed(std::vector<int>& arr);
int* split(std::vector<int>& arr_s,int m_begin,int m_end);
int select(std::vector<int>& arr_s,int k,int m_begin,int m_end);

int main()
{
    std::vector<int> arr={2,3,1,4,1,5,5,6};
    int r=searchMed(arr);
    std::cout<<r;
    return 0;
}


int searchMed(std::vector<int>& arr){
    //選出v  s split
    //選出k 剛開始是size/2 后來是(size-...)/2

    int r=select(arr,arr.size()/2,0,arr.size()-1);
    return r;
}

int* split(std::vector<int>& arr_s,int m_begin,int m_end){
    // TODO 把數(shù)組段第一個數(shù)劃分,并返回中間值的下標
    int a[2];
    int k=arr_s[m_begin];
    while (m_begin < m_end)
    {
        while (m_begin < m_end && arr_s[m_end] > k)
            m_end--;
        if (m_begin < m_end)
            arr_s[m_begin++] = arr_s[m_end];
        while (m_begin < m_end && arr_s[m_begin] < k)
            m_begin++;
        if (m_begin < m_end)
            arr_s[m_end--] = arr_s[m_begin];
    }
    arr_s[m_begin] = k;

    a[0]= m_begin; a[1] = m_end;

    while (arr_s[a[0] - 1] == k)
    {
        a[0]--;
    }
    while (arr_s[a[1] + 1] ==k)
    {
        a[1]++;
    }
    return a;//==的兩邊位置

}
int select(std::vector<int>& arr_s,int k,int m_begin,int m_end){
    //S< S=
    int item;

    if (m_end - m_begin < 2)
    {
        if (arr_s[m_begin] <arr_s[m_end])
            item = k == m_begin ? arr_s[m_begin] : arr_s[m_end];
        else
            item = k == m_end ? arr_s[m_end] : arr_s[m_begin];
        return item;
    }
    int s1,s2;//s1是<v的數(shù)的最后一個位置 s2是等于v的數(shù)的最后一個位置
    //srand( (unsigned)time( NULL ) );
   // int v= arr_s[rand()%arr_s.size()];
    int* a;

    a=split(arr_s,m_begin,m_end);//return s1,s2
    s1=a[0];
    s2=a[1];
    if(k<s1)
        item=select(arr_s,k,m_begin,s1-1);
        else
            if (k>=s1&&k<=s2)
                item=arr_s[s2];
            else item=select(arr_s,k,s2+1,m_end);
    return item;
}

2.尋找最鄰近的點對
【問題描述】
設(shè)p1=(x1,y1), p2=(x2,y2), … , pn=(xn,yn) 是平面上n個點構(gòu)成的集合S,設(shè)計和實現(xiàn)找出集合S中距離最近點對的算法。

//二維最鄰近點對問題
//#include "stdafx.h"
#include<time.h>
#include<iostream>
#include<cmath>
#include <stdlib.h>

using namespace std;
const int M = 50;

//用類PointX和PointY表示依x坐標和y坐標排好序的點
class PointX {
public:
    int operator<=(PointX a)const
    {
        return (x <= a.x);
    }
    int ID; //點編號
    float x, y; //點坐標
};

class PointY {
public:
    int operator<=(PointY a)const
    {
        return(y <= a.y);
    }
    int p; //同一點在數(shù)組x中的坐標
    float x, y; //點坐標
};

float Random();
template <class Type>
float dis(const Type&u, const Type&v);

bool Cpair2(PointX X[], int n, PointX& a, PointX& b, float& d);
void closest(PointX X[], PointY Y[], PointY Z[], int l, int r, PointX& a, PointX& b, float& d);

template <typename Type>
void Copy(Type a[], Type b[], int left, int right);

template <class Type>
void Merge(Type c[], Type d[], int l, int m, int r);

template <class Type>
void MergeSort(Type a[], Type b[], int left, int right);

int main()
{
    srand((unsigned)time(NULL));
    int length;

    cout << "請輸入點對數(shù):";
    cin >> length;

    PointX X[M];
    cout << "隨機生成的二維點對為:" << endl;

    for (int i = 0; i<length; i++)
    {
        X[i].ID = i;
        X[i].x = Random();
        X[i].y = Random();
        cout << "(" << X[i].x << "," << X[i].y << ") ";
    }

    PointX a;
    PointX b;
    float d;

    Cpair2(X, length, a, b, d);

    cout << endl;
    cout << "最鄰近點對為:(" << a.x << "," << a.y << ")和(" << b.x << "," << b.y << ") " << endl;
    cout << "最鄰近距離為: " << d << endl;

    return 0;
}

float Random()
{
    float result = rand() % 10000;
    return result*0.01;
}

//平面上任意兩點u和v之間的距離可計算如下
template <class Type>
inline float dis(const Type& u, const Type& v)
{
    float dx = u.x - v.x;
    float dy = u.y - v.y;
    return sqrt(dx*dx + dy*dy);
}

bool Cpair2(PointX X[], int n, PointX& a, PointX& b, float& d)
{
    if (n<2) return false;

    PointX* tmpX = new PointX[n];
    MergeSort(X, tmpX, 0, n - 1);

    PointY* Y = new PointY[n];
    for (int i = 0; i<n; i++) //將數(shù)組X中的點復(fù)制到數(shù)組Y中
    {
        Y[i].p = i;
        Y[i].x = X[i].x;
        Y[i].y = X[i].y;
    }

    PointY* tmpY = new PointY[n];
    MergeSort(Y, tmpY, 0, n - 1);

    PointY* Z = new PointY[n];
    closest(X, Y, Z, 0, n - 1, a, b, d);

    delete[]Y;
    delete[]Z;
    delete[]tmpX;
    delete[]tmpY;
    return true;
}
void closest(PointX X[], PointY Y[], PointY Z[], int l, int r, PointX& a, PointX& b, float& d)
{
    if (r - l == 1) //兩點的情形
    {
        a = X[l];
        b = X[r];
        d = dis(X[l], X[r]);
        return;
    }

    if (r - l == 2) //3點的情形
    {
        float d1 = dis(X[l], X[l + 1]);
        float d2 = dis(X[l + 1], X[r]);
        float d3 = dis(X[l], X[r]);

        if (d1 <= d2 && d1 <= d3)
        {
            a = X[l];
            b = X[l + 1];
            d = d1;
            return;
        }

        if (d2 <= d3)
        {
            a = X[l + 1];
            b = X[r];
            d = d2;
        }
        else {
            a = X[l];
            b = X[r];
            d = d3;
        }
        return;
    }

    //多于3點的情形,用分治法
    int m = (l + r) / 2;
    int f = l, g = m + 1;

    //在算法預(yù)處理階段,將數(shù)組X中的點依x坐標排序,將數(shù)組Y中的點依y坐標排序
    //算法分割階段,將子數(shù)組X[l:r]均勻劃分成兩個不想交的子集,取m=(l+r)/2
    //X[l:m]和X[m+1:r]就是滿足要求的分割。
    for (int i = l; i <= r; i++)
    {
        if (Y[i].p>m) Z[g++] = Y[i];
        else Z[f++] = Y[i];
    }

    closest(X, Z, Y, l, m, a, b, d);
    float dr;

    PointX ar, br;
    closest(X, Z, Y, m + 1, r, ar, br, dr);

    if (dr<d)
    {
        a = ar;
        b = br;
        d = dr;
    }

    Merge(Z, Y, l, m, r);//重構(gòu)數(shù)組Y

    //d矩形條內(nèi)的點置于Z中
    int k = l;
    for ( int i = l; i <= r; i++)
    {
        if (fabs(X[m].x - Y[i].x)<d)
        {
            Z[k++] = Y[i];
        }
    }

    //搜索Z[l:k-1]
    for (int i = l; i<k; i++)
    {
        for (int j = i + 1; j<k && Z[j].y - Z[i].y<d; j++)
        {
            float dp = dis(Z[i], Z[j]);
            if (dp<d)
            {
                d = dp;
                a = X[Z[i].p];
                b = X[Z[j].p];
            }
        }
    }
}

template <class Type>
void Merge(Type c[], Type d[], int l, int m, int r)
{
    int i = l, j = m + 1, k = l;
    while ((i <= m) && (j <= r))
    {
        if (c[i] <= c[j])
        {
            d[k++] = c[i++];
        }
        else
        {
            d[k++] = c[j++];
        }
    }

    if (i>m)
    {
        for (int q = j; q <= r; q++)
        {
            d[k++] = c[q];
        }
    }
    else
    {
        for (int q = i; q <= m; q++)
        {
            d[k++] = c[q];
        }
    }
}

template <class Type>
void MergeSort(Type a[], Type b[], int left, int right)
{
    if (left<right)
    {
        int i = (left + right) / 2;
        MergeSort(a, b, left, i);
        MergeSort(a, b, i + 1, right);
        Merge(a, b, left, i, right);//合并到數(shù)組b
        Copy(a, b, left, right);//復(fù)制回數(shù)組a
    }
}

template <typename Type>
void Copy(Type a[], Type b[], int left, int right)
{
    for (int i = left; i <= right; i++)
        a[i] = b[i];
}

實驗四
實驗?zāi)康呐c要求:掌握動態(tài)規(guī)劃方法的基本思想與設(shè)計策略。

1.多段圖中的最短路徑問題
【問題描述】
建立一個從源點S到終點T的多段圖,設(shè)計一個動態(tài)規(guī)劃算法求出從S到T的最短路徑值,并輸出相應(yīng)的最短路徑。

#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
using namespace std;
int  dp[100000],alone[100000],a[100000];
int main()
{
    int i,j,n,m;
    while(~scanf("%d",&m))
    {
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        memset(alone ,0,sizeof(alone));
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        int tmax;
        for(i=1;i<=m;i++)//分i段
        {
            tmax=-(1<<30);

            for(j=i;j<=n;j++)
            {
                dp[j]=max(dp[j-1],alone[j-1])+a[j];


                printf("%2d %2d %2d\n",a[j],alone[j-1],dp[j]);
                if(j>i)alone[j-1]=tmax;

                if(tmax<dp[j])tmax=dp[j];

            }

        }
        printf("%d\n",tmax);
    }
    return 0;
}

2.有向無環(huán)圖中的最短路徑問題
【問題描述】
建立一個從源點S到終點E的有向無環(huán)圖,設(shè)計一個動態(tài)規(guī)劃算法求出從S到E的最短路徑值,并輸出相應(yīng)的最短路徑。

/*Dijkstra求單源最短路徑 2010.8.26*/

#include <iostream>
#include <stack>
#include <stdlib.h>
#define M 100
#define N 100
using namespace std;

typedef struct node
{
    int matrix[N][M];      //鄰接矩陣
    int n;                 //頂點數(shù)
    int e;                 //邊數(shù)
}MGraph;

void DijkstraPath(MGraph g,int *dist,int *path,int v0)   //v0表示源頂點
{
    int i,j,k;
    bool *visited=(bool *)malloc(sizeof(bool)*g.n);
    for(i=0;i<g.n;i++)     //初始化
    {
        if(g.matrix[v0][i]>0&&i!=v0)
        {
            dist[i]=g.matrix[v0][i];
            path[i]=v0;     //path記錄最短路徑上從v0到i的前一個頂點
        }
        else
        {
            dist[i]=INT_MAX;    //若i不與v0直接相鄰,則權(quán)值置為無窮大
            path[i]=-1;
        }
        visited[i]=false;
        path[v0]=v0;
        dist[v0]=0;
    }
    visited[v0]=true;
    for(i=1;i<g.n;i++)     //循環(huán)擴展n-1次
    {
        int min=INT_MAX;
        int u;
        for(j=0;j<g.n;j++)    //尋找未被擴展的權(quán)值最小的頂點
        {
            if(visited[j]==false&&dist[j]<min)
            {
                min=dist[j];
                u=j;
            }
        }
        visited[u]=true;
        for(k=0;k<g.n;k++)   //更新dist數(shù)組的值和路徑的值
        {
            if(visited[k]==false&&g.matrix[u][k]>0&&min+g.matrix[u][k]<dist[k])
            {
                dist[k]=min+g.matrix[u][k];
                path[k]=u;
            }
        }
    }
}

void showPath(int *path,int v,int v0)   //打印最短路徑上的各個頂點
{
    stack<int> s;
    int u=v;
    while(v!=v0)
    {
        s.push(v);
        v=path[v];
    }
    s.push(v);
    while(!s.empty())
    {
        cout<<s.top()<<" ";
        s.pop();
    }
}

int main(int argc, char *argv[])
{
    int n,e;     //表示輸入的頂點數(shù)和邊數(shù)
    cout<<"輸入的頂點數(shù)和邊數(shù):";
    while(cin>>n>>e&&e!=0)
    {
        int i,j;
        int s,t,w;      //表示存在一條邊s->t,權(quán)值為w
        MGraph g;
        int v0;
        int *dist=(int *)malloc(sizeof(int)*n);
        int *path=(int *)malloc(sizeof(int)*n);
        for(i=0;i<N;i++)
            for(j=0;j<M;j++)
                g.matrix[i][j]=0;
        g.n=n;
        g.e=e;
        cout<<"輸入起始點,終點,權(quán)值"<<endl;
        for(i=0;i<e;i++)
        {
            cin>>s>>t>>w;
            g.matrix[s][t]=w;
        }
        cout<<"輸入源頂點";
        cin>>v0;        //輸入源頂點
        DijkstraPath(g,dist,path,v0);
        for(i=0;i<n;i++)
        {
            if(i!=v0)
            {
                showPath(path,i,v0);
                cout<<"dist"<<":"<<dist[i]<<endl;
            }
        }
    }
    return 0;
}

3.最長遞增子序列問題
【問題描述】
給定一個整數(shù)數(shù)組,設(shè)計一個動態(tài)規(guī)劃算法求出該數(shù)組中的最長遞增子序列。

#include <iostream>
using namespace std;


// 輸出LIS  序列
void outputLIS(int * arr, int index, int lis, int *L)
{
    //終止條件
    if (lis == 0 || index < 0)
        return;

    //找到第一個L[index]==lis
    while (L[index]!=lis && index>0)
        index--;

    //反序輸出
    if (index >= 0  && L[index]==lis)
    {
        outputLIS(arr, index - 1, lis - 1, L);
        cout << arr[index] << " ";
    }
}


int LIS(int *a, int n)
{
    //定義一個存取結(jié)果的數(shù)組
    int *L = new int[n];

    //填寫次序 0 to n-1
    for (int j = 0; j < n;j++)
    {
        L[j] = 1;//BaseCase
        //find max L[i]
        for (int i = 0; i < j;i++)
        {
            if (a[i]<a[j] && L[i]+1 > L[j])
            {
                L[j] = L[i] + 1;
            }
        }
    }

    //return the max of L[0~n-1]
    int max = L[0];
    for (int i = 0; i < n; i++)
    {
        //cout << L[i] << "  ";
        if (L[i]>max)
        {
            max = L[i];
        }
    }

    //回溯輸出
    cout << "最長遞增子序列如下:";
    outputLIS(a, n,max, L);

    return max;
}

int main()
{
    int a[] = { 5, 2, 3, 2 ,8, 6, 2, 4, 5, 7};
    for(int i=0;i<10;i++)
        cout<<a[i]<<" ";
    cout<<endl;
    int n = sizeof(a) / sizeof(int);
    cout<<endl<<"長度為:" << LIS(a, n) << endl;
    return 0;
}

4.矩陣連乘問題
【問題描述】
給定n個矩陣{A1,A2,…,An},其中AiAi+1是可乘的,i=1,2,…,n-1,考察這n個矩陣的連乘積A1A2…An,設(shè)計一個動態(tài)規(guī)劃算法,求出這個矩陣連乘積問題的最優(yōu)計算順序。
實現(xiàn)要求:隨機生成n個合法的可連乘的矩陣,以完全加括號的方式輸出其最優(yōu)計算順序。

#include <iostream>
using namespace std;

const int L = 7;

int RecurMatrixChain(int i,int j,int **s,int *p);//遞歸求最優(yōu)解
void Traceback(int i,int j,int **s);//構(gòu)造最優(yōu)解

int main()
{
    int p[L]={30,35,15,5,10,20,25};

    int **s = new int *[L];
    for(int i=0;i<L;i++)
    {
        s[i] = new int[L];
    }

    cout<<"矩陣的最少計算次數(shù)為:"<<RecurMatrixChain(1,6,s,p)<<endl;
    cout<<"矩陣最優(yōu)計算次序為:"<<endl;
    Traceback(1,6,s);
    return 0;
}

int RecurMatrixChain(int i,int j,int **s,int *p)
{
    if(i==j) return 0;
    int u = RecurMatrixChain(i,i,s,p)+RecurMatrixChain(i+1,j,s,p)+p[i-1]*p[i]*p[j];
    s[i][j] = i;

    for(int k=i+1; k<j; k++)
    {
        int t = RecurMatrixChain(i,k,s,p) + RecurMatrixChain(k+1,j,s,p) + p[i-1]*p[k]*p[j];
        if(t<u)
        {
            u=t;
            s[i][j]=k;
        }
    }
    return u;
}

void Traceback(int i,int j,int **s)
{
    if(i==j) return;
    Traceback(i,s[i][j],s);
    Traceback(s[i][j]+1,j,s);
    cout<<"("<<i<<","<<s[i][j]<<")";
    cout<<"*("<<(s[i][j]+1)<<","<<j<<")"<<endl;
}

實驗五

實驗?zāi)康呐c要求:掌握動態(tài)規(guī)劃方法的基本思想與設(shè)計策略。

1.最長公共子序列問題
【問題描述】
⑴ 給定兩個字符串X和Y,設(shè)計一個動態(tài)規(guī)劃算法,求出這兩個字符串的最長公共子序列,并輸出該子序列。

⑵ 若僅要求求出兩個字符串的最長公共子序列的長度值,為節(jié)省存儲空間,采用“滾動數(shù)組”方式實現(xiàn)動態(tài)規(guī)劃算法。

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;

bool isodd(int i)
{
    if(i%2==0)
        return false;
    else
        return true;
}

int main()
{
    vector<char> a={'a','c','b','b','a'};
    vector<char> b={'a','c','d','b','c','d','b'};
    int bsize=b.size();
    int dp[2][bsize];
    memset(dp,0,sizeof(dp));
    for(int i=0;i<a.size();i++)
        for(int j=0;j<b.size();j++)
            if(!isodd(i))//i是偶數(shù)
                {
                if(a[i]==b[j])
                    if(i==0||j==0) dp[1][j]=1;
                        else dp[1][j]=dp[0][j-1]+1;
                else
                    if(i==0||j==0) dp[1][j]=dp[0][j];
                        else dp[1][j]=max(dp[0][j],dp[1][j-1]);
                }
            else
            {
                if(a[i]==b[j])
                    if(i==0||j==0) dp[0][j]=1;
                        else dp[0][j]=dp[1][j-1]+1;
                else
                    if(i==0||j==0) dp[0][j]=dp[1][j];
                        else dp[0][j]=max(dp[1][j],dp[0][j-1]);
            }
    cout<<"最長公共子序列:";
    if(isodd(a.size()))
        cout<<dp[1][b.size()-1];
    else
        cout<<dp[0][b.size()-1];
        return 0;
}

2.0-1背包問題
【問題描述】
給定n種物品和一背包。物品i的重量是wi,其價值為vi,背包的容量為W(假定物品重量與背包容量值均為整數(shù)),應(yīng)如何選擇裝入背包中的物品,使得裝入背包中物品的總價值最大?設(shè)計一個動態(tài)規(guī)劃算法,求解背包問題。
K(w,j)=max{k(w-wj,j-1)+vj,k(w,j-1)}

#include <iostream>
#include <cstring>
using namespace std;

#define W 50

void Trackback(int *weight, int n, int w,bool *p,int **a)
{
    if (n==0 || w==0)
        return;
    if (a[w][n]==a[w][n-1])//若和左邊的一致,說明沒有選最后一個
    {
        p[n - 1] = false;
        Trackback(weight, n - 1, w, p, a);
    }
    else
    {
        p[n - 1] = true;
        Trackback(weight, n - 1, w-weight[n-1], p, a);
    }
}
int getMaxValue(int w, int n, int *price, int * weight )
{
    //創(chuàng)建一個 w+1 * n+1 的二維表
    int **a = new int *[w + 1];
    for (int i = 0; i < w + 1;i++)
    {
        a[i] = new int[n + 1];
    }

    //創(chuàng)建一個數(shù)組 記錄貨物是否取的狀態(tài)
    bool  *p = new bool[w];
    memset(p, false, sizeof(p));

    //base case
    for (int i = 0; i < w + 1; i++)
        a[i][0] = 0;
    for (int i = 0; i < n + 1; i++)
        a[0][i] = 0;

    //for
    for (int i = 1; i < w + 1;i++)
    {
        for (int j = 1; j < n + 1;j++)
        {
            if (i<weight[j-1])//填寫a[i][j],若當(dāng)前背包重量小于物品,則不裝
            {
                a[i][j] = a[i][j - 1];
            }
            else
            {
                if (a[i][j-1] <= a[i-weight[j-1]][j-1] + price[j-1])
                {
                    a[i][j] = a[i - weight[j - 1]][j - 1] + price[j - 1] ;
                }
                else
                    a[i][j] = a[i][j - 1];
            }
        }
    }

    Trackback(weight, n, w, p, a);
    cout << "從左到右是否取件為:";
    for (int i = 0; i < n; i++)
        cout << p[i] << " ";
    cout << endl;
    return a[w][n];
}

int main()
{
    int price[] = { 300, 100, 30 ,200};
    int weight[] = { 10, 20, 30 ,5};
    cout << "背包問題的解是:"<<getMaxValue(W, 4, price, weight) << endl;
    return 0;
}

實驗六
實驗?zāi)康呐c要求:
(1) 掌握樹型動態(tài)規(guī)劃方法的基本思想與設(shè)計策略;
(2) 掌握貪心法的基本思想和設(shè)計方法。

1.樹中的最大獨立集問題
【問題描述】
給定一個無回路的無向圖(即樹),設(shè)計一個動態(tài)規(guī)劃算法,求出該圖的最大獨立集,并輸出該集合中的各個頂點值。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;

const int MAXN=100;
vector<int> G[MAXN]; //G[i]表示頂點i的鄰接點
int l[MAXN]; //結(jié)點層次
int p[MAXN]; //根樹
int dp[MAXN]; //dp數(shù)組
int sumC[MAXN]; //孩子DP和
int sumS[MAXN]; //孫子DP和
int maxL; //最大層次
int n;



void readTree()
{
    int u,v;
    cin>>n;
    for(int i=0;i<n-1;++i)
    {
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
}

void dfs(int u,int fa)
{
    int d=G[u].size();
     l[u]= (fa==-1)? 0: (l[fa]+1);
     if(l[u]>maxL)
     {
         maxL=l[u];
     }
    for(int i=0;i<d;++i)
    {
        int v=G[u][i];
        if(v!=fa)
        {
            dfs(v,p[v]=u);
        }
    }
}

int rootDp(int u)
{
    //構(gòu)造u根樹
    p[u]=-1;
    maxL=-1;
    dfs(u,p[u]);
    for(int i=maxL;i>=0;--i)
    {
        for(int j=0;j<n;++j)
        {
            if(l[j]==i)
            {
                if (sumS[j]+1>sumC[j])
                {
                    dp[j]=sumS[j]+1;
                }
                else
                    dp[j] = sumC[j];
                if(i-1>=0)
                {
                    sumC[p[j]]+=dp[j];
                }
                if(i-2>=0)
                {
                    sumS[p[p[j]]]+=dp[j];
                }
            }
        }
    }
    return dp[u];
}

int main()
{
    readTree();
    int res=-1;
    //分別以每個頂點為根
    for(int i=0;i<n;++i)
    {
        memset(sumS,0,sizeof(sumS));
        memset(sumC,0,sizeof(sumC));
        int tmp;
        if((tmp=rootDp(i))>res)
            res=tmp;
    }
    cout<<res<<endl;
    return 0;
}

2.區(qū)間調(diào)度問題
【問題描述】
給定n個活動,其中的每個活動ai包含一個起始時間si與結(jié)束時間fi。設(shè)計與實現(xiàn)算法從n個活動中找出一個最大的相互兼容的活動子集S。

要求:分別設(shè)計動態(tài)規(guī)劃與貪心算法求解該問題。其中,對貪心算法分別給出遞歸與迭代兩個版本的實現(xiàn)。

貪心算法

#include <iostream>
using namespace std;

//i是上一個符合條件的id,為了完整性,在第一列加上-1,n是總數(shù)目
void GetSet(int *si, int *fi, int i, int n)
{
    int m = i + 1;
    while (m <= n && si[m] < fi[i])//找第一個符合的
        m = m + 1;
    if (m <= n)
    {
        cout <<m<<" ";
        GetSet(si, fi, m, n);
    }
}

int main()
{
    int si[] = { -1,1, 3, 0, 5, 3, 5, 6, 8, 8, 2};
    int fi[] = { -1,4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
    int n = 11;
    cout<<"兼容的活動為:";
    GetSet(si, fi, 0, 10);
}
#include<iostream>
using namespace std;

//動態(tài)規(guī)劃實現(xiàn)
int GetSet(int *start, int *finish, int n)
{
    //c[i][j]表示第i個工作結(jié)束后到第j個工作開始前之間存在的可兼容的工作個數(shù)
    //new c[i][j]
    int **c = new int *[n];
    for(int i=0; i<n; i++)
        c[i] = new int[n];

    //c[i][j]初始賦值
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            c[i][j] = 0;

    for(int j=0; j<n; j++)
    {
        for(int i=0; i<n; i++)
        {
            if(i<j)
            {
                int s = finish[i];
                int f = start[j];
                for(int k=i+1; k<j; k++)
                    if(start[k]>=s && finish[k]<=f)
                    {
                        if(c[i][j]<(c[i][k]+c[k][j]+1))
                            c[i][j] = c[i][k]+c[k][j]+1; //分解成更小子問題
                    }
            }
        }
    }
    return c[0][n-1];   //最終目標
}

int main()
{
    //已經(jīng)按完成時間排好序
    int start[] = {-1,3,0,5,3,5,6,8,8,2,1000};
    int finish[] = {0,5,6,7,8,10,10,11,12,13,10000};
    int n = 11; //活動只有9個
    cout<<"最大兼容活動子集的大小為:"<<GetSet(start, finish, n)<<endl;
    return 0;
}

實驗七

實驗?zāi)康呐c要求:
(1) 掌握貪心法的基本思想和設(shè)計方法。

1.區(qū)間劃分問題
【問題描述】
給定一組報告,其中的每個報告設(shè)置了一個開始時間si和結(jié)束時間fi。設(shè)計與實現(xiàn)一個算法,對這組報告分配最少數(shù)量的教室,使得這些報告能無沖突的舉行。

#include <iostream>
#include <stdlib.h> 
using namespace std;

#define N 100

struct Report
{
    int num;//報告編號
    int begin;//開始時間
    int end;//結(jié)束時間
    bool flag;//是否已分配教室
    int classroom;//教室號
};

void QuickSort(Report* rep,int f,int t)//一開始將所有報告按結(jié)束時間排序
{
    if(f<t)
    {
        int i=f-1,j=f;
        Report r=rep[t];
        while(j<t)
        {
            if(rep[j].end<=r.end)
            {
                i++;
                Report tmp=rep[i];
                rep[i]=rep[j];
                rep[j]=tmp;
            }
            j++;
        }
        Report tmp1=rep[t];
        rep[t]=rep[i+1];
        rep[i+1]=tmp1;
        QuickSort(rep,f,i);
        QuickSort(rep,i+2,t);
    }
}

int select_room(Report *rep,int *time,int n)
{
    //第一個報告分給第一個教室
    int i=1,j=1;//i報告,j教室
    int sumRoom=1;
    int sumRep=1;//教室已分配的報告數(shù)
    time[1]=rep[0].end;
    rep[0].classroom=1;

    for(i=1;i<n;i++)
    {
        for(j=1;j<=sumRoom;j++)
        {
            if((rep[i].begin>=time[j])&&(!rep[i].flag))
            {
                rep[i].classroom=j;
                rep[i].flag=true;
                time[j]=rep[i].end;
                sumRep++;
            }
        }
        if(sumRep<n&&i==n-1)//報告沒有分配完
        {
            i=0;
            sumRoom++;
        }
    }
    return sumRoom;
}

int main()
{
    int n;
    Report rep[N];
    int time[N];//每個教室最后一個報告的結(jié)束時間
    cout<<"請輸入報告數(shù)量:"<<endl;
    cin>>n;
    int i;
    for(i=0;i<n;i++)
    {
        //初始化
        time[i+1]=0;//time[1]~time[10]
        rep[i].num=i+1;//編號1~10
        rep[i].flag=false;
        rep[i].classroom=0;

        cout<<"報告"<<i+1<<"開始時間:";
        cin>>rep[i].begin;
        cout<<"報告"<<i+1<<"結(jié)束時間:";
        cin>>rep[i].end;
    }
    QuickSort(rep,0,n-1);
    int roomNum=select_room(rep,time,n);
    cout<<"所用教室總數(shù)為:"<<roomNum<<endl;
    for(i=0;i<n;i++)
    {
        cout<<"活動"<<rep[i].num<<"在教室"<<rep[i].classroom<<"中"<<endl;
    }
    system("pause");
    return 0;
}

2.最小延遲調(diào)度問題
【問題描述】
假定有一單個的資源在一個時刻只能處理一個任務(wù)?,F(xiàn)給定一組任務(wù),其中的每個任務(wù)i包含一個持續(xù)時間ti和截止時間di。設(shè)計與實現(xiàn)一個算法,對這組任務(wù)給出一個最優(yōu)調(diào)度方案,使其對所有任務(wù)的最大延遲最小化。

#include<iostream>
using namespace std;

#define N 100

struct Mission
{
    int num;
    int last;
    int end;
};

void QuickSort(Mission *mi,int f,int t)
{
    if(f<t)
    {
        int i=f-1,j=f;
        Mission m=mi[t];
        while(j<t)
        {
            if(mi[j].end<=m.end)
            {
                i++;
                Mission tmp=mi[i];
                mi[i]=mi[j];
                mi[j]=tmp;
            }
            j++;
        }
        Mission tmp1=mi[t];
        mi[t]=mi[i+1];
        mi[i+1]=tmp1;
        QuickSort(mi,f,i);
        QuickSort(mi,i+2,t);
    }
}

int main()
{
    int n;
    cout<<"請輸入任務(wù)總數(shù):"<<endl;
    cin>>n;
    Mission mi[N];//Mission[0]~Mission[n-1]
    int start[N+1];//排好序的任務(wù)的開始時間,start[1]~start[n]
    for(int i=0;i<n;i++)
    {
        mi[i].num=i+1;
        cout<<"任務(wù)"<<i+1<<"的持續(xù)時間為:";
        cin>>mi[i].last;
        cout<<"任務(wù)"<<i+1<<"的截止時間為:";
        cin>>mi[i].end;
    }
    QuickSort(mi,0,n-1);
    int delay=0;
    start[1]=0;

    if(start[1]+mi[0].last>mi[0].end)
    {
        delay+=start[1]+mi[0].last-mi[0].end;//如果開始時間+持續(xù)時間>截止時間,累計延遲
    }
    for(int i=1;i<n;i++)
    {
        start[i+1]=start[i]+mi[i-1].last;
        if(start[i+1]+mi[i].last>mi[i].end)
        {
            delay+=start[i+1]+mi[i].last-mi[i].end;
        }
    }
    cout<<"延遲最小為:"<<delay<<endl;
    for(int i=0;i<n;i++)
    {
        cout<<"任務(wù)"<<mi[i].num<<"的執(zhí)行時間:["<<start[i+1]<<","<<mi[i].last+start[i+1]<<"]"<<endl;
    }
}

實驗八

實驗?zāi)康呐c要求:
(2) 掌握使用圖的深度優(yōu)先搜索算法實現(xiàn)對有向圖中是否包含環(huán)的判斷;
(3) 掌握使用圖的深度優(yōu)先搜索算法實現(xiàn)對有向圖的強連通分量的劃分。

1.有向圖中環(huán)的判斷問題
【問題描述】
給定一個有向圖,要求使用深度優(yōu)先搜索策略,判斷圖中是否存在環(huán)。

#include<iostream>
#include<malloc.h>
using namespace std;
#define maxNum 100 //定義鄰接舉證的最大定點數(shù)
int pre[maxNum];
int post[maxNum];
int point=0;//pre和post的值
bool is_DAG=true;//標識位,表示有向無環(huán)圖
/*
0 白色,未被訪問過的節(jié)點標白色
-1 灰色,已經(jīng)被訪問過一次的節(jié)點標灰色
1 黑色,當(dāng)該節(jié)點的所有后代都被訪問過標黑色
時間復(fù)雜度:O(n+e)
*/
int color[maxNum];//頂點顏色表 color[u]
//圖的鄰接矩陣表示結(jié)構(gòu)
typedef struct
{
    char v[maxNum];//圖的頂點信息
    int e[maxNum][maxNum];//圖的頂點信息
    int vNum;//頂點個數(shù)
    int eNum;//邊的個數(shù)
}graph;
void createGraph(graph *g);//創(chuàng)建圖g
void DFS(graph *g);//深度優(yōu)先遍歷圖g
void dfs(graph *g,int i);//從頂點i開始深度優(yōu)先遍歷與其相鄰的點
void dfs(graph *g,int i)
{
    //cout<<"頂點"<<g->v[i]<<"已經(jīng)被訪問"<<endl;
    cout<<"頂點"<<i<<"已經(jīng)被訪問"<<endl;
    color[i]=-1;
    pre[i]=++point;
    for(int j=1;j<=g->vNum;j++)
    {
        if(g->e[i][j]!=0)
        {
            if(color[j]==-1)//探索到回邊,存在環(huán)
            {
                is_DAG=false;//不是有向無環(huán)圖
            }
            else if(color[j]==0)
                dfs(g,j);
        }
    }
    post[i]=++point;
    color[i]=1;//表示i的后裔節(jié)點都被訪問過
}
void DFS(graph *g)
{
    int i;
    //初始化color數(shù)組,表示一開始所有頂點都未被訪問過,//初始化pre和post
    for(i=1;i<=g->vNum;i++)
    {
        color[i]=0;
        pre[i]=0;
        post[i]=0;
    }
    //深度優(yōu)先搜索
    for(i=1;i<=g->vNum;i++)
    {
        if(color[i]==0)//如果這個頂點為被訪問過,則從i頂點出發(fā)進行深度優(yōu)先遍歷
        {
            dfs(g,i);

        }
    }
}
void createGraph(graph *g)//創(chuàng)建圖g
{
    cout<<"請輸入頂點個數(shù):";
    cin>>g->vNum;
    cout<<"請輸入邊的個數(shù):";
    cin>>g->eNum;
    int i,j;
    //初始畫圖g
    for(i=1;i<=g->vNum;i++)
        for(j=1;j<=g->vNum;j++)
            g->e[i][j]=0;
    //輸入邊的情況
    cout<<"請輸入邊的頭和尾"<<endl;
    for(int k=1;k<=g->eNum;k++)
    {
        cin>>i>>j;
        g->e[i][j]=1;
    }
}
int main()
{
    graph *g;
    g=(graph*)malloc(sizeof(graph));
    createGraph(g);//創(chuàng)建圖g
    DFS(g);//深度優(yōu)先遍歷
    //各頂點的pre和post值
   // for(int i=1;i<=g->vNum;i++)
      //  cout<<"頂點"<<i<<"的pre和post分別為:"<<pre[i]<<" "<<post[i]<<endl;
    //判斷是否是有向無環(huán)圖
    if(is_DAG)
        cout<<"有向無環(huán)圖"<<endl;
    else
        cout<<"存在環(huán)"<<endl;
    int k;
    cin>>k;
    return 0;
}

2.有向圖的強連通分量問題
【問題描述】
給定一個有向圖,設(shè)計一個算法,求解并輸出該圖的各個強連通分量。

#include <iostream>
#include <stack>
using namespace std;

#define MAX_VERTEX_SIZE 10001
struct EdgeNode{
    int vertex;
    EdgeNode *nextArc;
};

struct VerTexNode{
    EdgeNode* firstArc;
};

struct Graph{
    int n,e;
    VerTexNode vNode[MAX_VERTEX_SIZE];
};

int time = 0;
int low[MAX_VERTEX_SIZE];
int dfn[MAX_VERTEX_SIZE];
int visited[MAX_VERTEX_SIZE];
int inStack[MAX_VERTEX_SIZE];
stack<int> st;
Graph graph;

void initeGraph(int n,int m)
{
    for(int i = 1;i<=n;i++)
    {
        graph.vNode[i].firstArc = NULL;
    }
    graph.n = n;
    graph.e = m;

}

//頭插法建立圖
void creatGraph(int s,int v)
{
    EdgeNode *edgeNode = new EdgeNode;
    edgeNode->vertex = v;
    edgeNode->nextArc = graph.vNode[s].firstArc;
    graph.vNode[s].firstArc = edgeNode;
}

int min(int a,int b)
{
    if(a>b)
        return b;
    else
        return a;
}

void trajan(int u)
{
    dfn[u] = low[u] = time++;
    st.push(u);
    visited[u] = 1;
    inStack[u] = 1;
    EdgeNode *edgePtr = graph.vNode[u].firstArc;
    while(edgePtr !=NULL)
    {
        int v = edgePtr->vertex;
        if(visited[v] == 0)
        {
            trajan(v);
            low[u] = min(low[u],low[v]);
        }
        else
        {
            low[u] = min(low[u],dfn[v]);
        }
        edgePtr = edgePtr->nextArc;
    }

    if(dfn[u] == low[u])
    {
        int vtx;
        cout<<"set is: ";
        do{
            vtx = st.top();
            st.pop();
            inStack[vtx] = 0;//表示已經(jīng)出棧
            cout<<vtx<<' ';
        }while(vtx !=u );
    }

}

int main()
{
    int n,m;
    int s,a;
    cout<<"vexs and edges:"<<endl;
    cin>>n>>m;
    initeGraph(n,m);
    for(int i = 1;i<=n;i++)
    {
        visited[i] = 0;
        inStack[i] = 0;
        dfn[i] = 0;
        low[i] = 0;
    }
    cout<<"the begin and the end of each edge:"<<endl;
    for(int j = 1;j<=m;j++)
    {
        cin>>s>>a;
        creatGraph(s,a);
    }

    for(int i =1;i<=n;i++)
        if(visited[i] == 0)
            trajan(i);
    return 0;
}

實驗九

實驗?zāi)康呐c要求:
(4) 理解與掌握求解最大流與最小割的基本算法。
(5) 學(xué)會應(yīng)用最大流與最小割算法解決實際問題。

1.實現(xiàn)Ford-Fulkerson算法,求出給定圖中從源點s到匯點t的最大流,并輸出最小割。

#include<queue>
#include<iostream>
#include<string.h>
using namespace std;
#define MAX 1024
int nodes,edges;

int capacity[MAX][MAX];//記錄邊的當(dāng)前還可以通過的最大流量
int maxflow=0;
bool isVisited[MAX];//在BFS或DFS找增廣路的時候記錄該元素是否訪問過
int pre[MAX];//記錄節(jié)點的前一個節(jié)點

/*
    我最疑惑的地方是capacity[i][pre[i]]+=increase;這個地方。
    我們一開始以為這只是一個簡單的有向圖,其實不是,這個有向圖會根據(jù)它的兩個節(jié)點之間的通過的流量自動改變
        我們可以把它看成是最原始的有向圖中有箭頭的兩個節(jié)點可以相互通過流,而不僅僅是沿箭頭的方向通過流(通過判斷兩個節(jié)點之間的最大
        流量來判斷。)

    表達能力實在有限。。我自己都覺得沒說清楚..
*/
inline int min(int a,int b)
{
    return a>b?b:a;
}

bool DFS(int src)
{
    if(!src)
        pre[src]=-1;
    if(src==nodes-1)
        return true;
    isVisited[src]=true;
    for(int i=0;i<nodes;i++)
    {
        if(!isVisited[i]&&capacity[src][i])
        {
            isVisited[i]=true;
            pre[i]=src;
            if(DFS(i))
                return true;
        }
    }
    return false;
}

bool BFS()
{
    queue<int> myQueue;
    myQueue.push(0);
    isVisited[0]=true;
    pre[0]=-1;
    while(!myQueue.empty())
    {
        int current=myQueue.front();
        myQueue.pop();
        for(int i=0;i<nodes;i++)
        {
            if(!isVisited[i]&&capacity[current][i])
            {
                myQueue.push(i);
                pre[i]=current;
                isVisited[i]=true;
            }
        }
    }

    return isVisited[nodes-1];
}

void MaxFlow()
{
    while(1)
    {
        memset(isVisited,false,nodes);
        memset(pre,0xff,4*nodes);

    //  if(!DFS(0))
    //      break;
        if(!BFS())
            break;

        int increase=MAX;
        int i;
        for(i=nodes-1;pre[i]>=0;i=pre[i])
        {
            increase=min(increase,capacity[pre[i]][i]);
        }
        for(i=nodes-1;pre[i]>=0;i=pre[i])
        {
            capacity[pre[i]][i]-=increase;
            capacity[i][pre[i]]+=increase;
        }
        maxflow+=increase;


    }
}
int main()
{
    while(1)
    {
        cout<<"vexs edges:"<<endl;
        cin>>nodes>>edges;
        int firstnode,secondenode,capa;
        for(int i=0;i<edges;i++)
        {
            cin>>firstnode>>secondenode>>capa;
            capacity[firstnode][secondenode]=capa;
        }
        MaxFlow();
        cout<<"最大流:"<<maxflow<<endl;
        maxflow=0;

    }
    return 0;
}

2.設(shè)計與實現(xiàn)二部圖匹配(Bipartite Matching)問題的算法。

//void *memset(void *s,int c,size_t n)將已開辟內(nèi)存空間 s 的首 n 個字節(jié)的值設(shè)為值 c
//#include <Ford_Fulkerson>
#include <algorithm>
#include <cstdio>
#include <list>
#include <queue>
#include <iostream>
using namespace std;
#define INFI 1000
typedef struct _mark
{
    int pre_suc;
    int max_incr;
}MARK;
int iteration = 0;//增光路徑數(shù)目
const int N = 100;
list<int> setS;
bool isMark[N], isCheck[N], isDone;
MARK markList[N];
int c[N][N], f[N][N];
int n; //頂點數(shù)
int Maxflow()
{
    int flow = 0;
    for (int i = 0; i<n; i++)
    {
        flow += f[0][i];
    }
    return flow;
}
void Mincut()//isMark的點就是最小割
{
    int i = 0;
    while (i<n)
    {
        if (isMark[i])
            setS.push_back(i);
        i++;
    }
}
int IncrFlowAuxi(int index)//計算增廣路徑中的最大可增量
{
    if (index == 0)
        return markList[index].max_incr;
    int prev = markList[index].pre_suc;
    int maxIncr = markList[index].max_incr;
    return min(maxIncr, IncrFlowAuxi(prev));//遞歸求瓶頸值為最大增量
}
void IncrFlow()//增廣路徑的增加
{
    iteration++;
    int incr = IncrFlowAuxi(n - 1); //最大可增量
    int index = n - 1;
    int prev;
    while (index != 0)
    {
        if (index != n - 1)
            cout << index << " ";
        prev = markList[index].pre_suc;
        f[prev][index] += incr; //增廣路徑增加后,相應(yīng)的流量進行更新
        index = prev;
    }
    cout << endl;
}
void Mark(int index, int pre_suc, int max_incr)//被標記表示可能被納入新路徑
{
    isMark[index] = true;
    markList[index].pre_suc = pre_suc;//前驅(qū)
    markList[index].max_incr = max_incr;//當(dāng)前路徑的流值
}
void Check(int i)//被mark且被check的點表示已經(jīng)被納入新路徑
{
    isCheck[i] = true;
    for (int j = 0; j<n; j++)
    {
        if (c[i][j]>0 && !isMark[j] && c[i][j]>f[i][j])//forward 邊
            Mark(j, i, min(markList[i].max_incr, c[i][j] - f[i][j]));
        if (c[j][i]>0 && !isMark[j] && f[j][i]>0)//reverse 邊
            Mark(j, i, min(markList[i].max_incr, f[j][i]));
    }
}
//ford_fulkerson算法
int ford_fulkerson()
{
    int i;
    while (1)//一次循環(huán)找到一個新路徑
    {
        isDone = true;
        i = 0;
        while (i<n)//一次循環(huán)判斷上次循環(huán)是否有找到新路徑,若無則表明沒有新路徑,終止算法
        {
            if (isMark[i] && !isCheck[i])  //判斷是否所有標記的點都已被檢查:若是,結(jié)束整個算法
            {
                isDone = false;
                break;
            }
            i++;
        }
        if (isDone) //算法結(jié)束,則計算最小割和最大流
        {
            Mincut();
            return Maxflow();
        }
        while (i<n)//貪心法構(gòu)建新路徑
        {
            if (isMark[i] && !isCheck[i]) {
                Check(i);
                i = 0;
            }
            if (isMark[n - 1]) //如果匯t被標記,說明找到了一條增廣路徑,則增加該條路徑的最大可增加量
            {
                IncrFlow();
                memset(isMark + 1, false, n - 1); //增加該增廣路徑后,除了源s,其余標記抹去
                memset(isCheck, false, n);
            }
            else i++;
        }
    }
}
int main()
{
    //測試數(shù)據(jù)為ppt第40頁的圖,只實現(xiàn)了二部圖的最大匹配
    n = 12;
    for (int k = 0; k < n; ++k)
    {
        memset(c[k], 0, sizeof(c[0][0])*n);
        memset(f[k], 0, sizeof(f[0][0])*n);  //初始各分支流量為0
        memset(isMark, false, n);
        memset(isCheck, false, n);
    }
    isMark[0] = true; //給源做永久標記
    markList[0].max_incr = INFI;
    markList[0].pre_suc = INFI;
    c[1][6] = INFI;
    c[1][7] = INFI;
    c[2][7] = INFI;
    c[3][6] = INFI;
    c[3][8] = INFI;
    c[3][9] = INFI;
    c[4][7] = INFI;
    c[4][10] = INFI;
    c[5][7] = INFI;
    c[5][10] = INFI;
    for (int i = 1; i < n / 2; i++)
        c[0][i] = 1;
    for (int i = n/2; i < n-1; i++)
        c[i][n-1] = 1;
    cout << "最大匹配結(jié)果為:" << endl;
    int result= ford_fulkerson();
    cout << "匹配邊個數(shù)為:" << result << endl;
    system("PAUSE");
}

3.設(shè)計與實現(xiàn)項目選擇(Project Selection)問題的算法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、用C語言實現(xiàn)一個revert函數(shù),它的功能是將輸入的字符串在原串上倒序后返回。 2、用C語言實現(xiàn)函數(shù)void ...
    希崽家的小哲閱讀 6,690評論 0 12
  • 佟月月度假回來。 月月,你可回來了,大伙都挺想你的…… “嗯,回來了,我也想大家了”。 辦公室里一陣陣寒暄聲。 方...
    研橙閱讀 260評論 0 0
  • global variable(全局變量)global variable(全局變量),這個概念基本上在所有的主流編...
    constant007閱讀 4,408評論 0 1
  • 楓葉 火紅火紅 欲燒 所有一切 小徑 幽靜幽靜 隱退 一切塵緣 撐一傘憂郁 獨自 徘徊于 冷清的雨徑上 踏著...
    企鵝的北極星閱讀 288評論 0 0
  • 今天陪孩子大半天,早上陪她去擊劍,中午帶她去商場玩,去玩游戲,我認為孩子就是喜歡玩游戲的,帶她去剪娃娃,打游戲。她...
    D不瘋不成魔D閱讀 218評論 2 0

友情鏈接更多精彩內(nèi)容