zoukankan      html  css  js  c++  java
  • 区间DP

    区间DP

    两个难点:

    1、枚举所有可能的区间

    2、状态转移方程

    经典问题1:石子合并

    1274:【例9.18】合并石子
    
    时间限制: 1000 ms         内存限制: 65536 KB
    提交数: 5121     通过数: 3243 
    【题目描述】
    在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
    
    计算出将N堆石子合并成一堆的最小得分。
    
    【输入】
    第一行为一个正整数N (2≤N≤100);
    
    以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1≤i≤N)。
    
    【输出】
    一个正整数,即最小得分。
    
    【输入样例】
    7
    13
    7
    8
    16
    21
    4
    18
    【输出样例】
    239
    合并石子

    状态:f[i][j]表示区间i到j之间的最小花费,那么f[1][n]就是最后的答案

    状态转移方程:f[i][j]=f[i][k]+f[k][j]+sum[i][j-i+1];

    所以在这里需要求一个前缀和sum[i][j-i+1] 就相当于sum[j]-sum[i-1]

    #include <bits/stdc++.h>
    using namespace std;
    const int N=130,inf=0x3f3f3f;; 
    int sum[N],c[N];
    int f[N][N],n,x,t; 
    inline int read()
    {
        int x=0;char y='*',z=getchar();
        while(z<'0'||z>'9') y=z,z=getchar();
        while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar();
        return y=='-'?-x:x;
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++){
            x=read(); 
            sum[i]=sum[i-1]+x;//前缀和
        } 
        memset(f,127/3,sizeof(f));
        for(int i=1;i<=n;i++) f[i][i]=0;
        for(int i=n-1;i>=1;i--)
            for(int j=i+1;j<=n;j++)
                for(int k=i;k<=j-1;k++)
                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
        cout<<f[1][n]<<endl;    
        return 0;
    }

    上述代码有三重循环,所以复杂度为O(n3),所以这种问题只能处理n<250的问题;

    该问题在动态规划中也有写过,咳咳还有一个环形的情况

    优化:

    平行四边形优化原理:是区间DP常用的优化方法;主要思想就是用s[i][j]来存储最优分割点

    只需要更改几个地方:(红色)!!

    #include <bits/stdc++.h>
    using namespace std;
    const int N=130,inf=0x3f3f3f;; 
    int sum[N],c[N],s[N][N];
    int f[N][N],n,x,t; 
    inline int read()
    {
        int x=0;char y='*',z=getchar();
        while(z<'0'||z>'9') y=z,z=getchar();
        while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar();
        return y=='-'?-x:x;
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++){
            x=read(); 
            sum[i]=sum[i-1]+x;//前缀和
        } 
        memset(f,127/3,sizeof(f));
        for(int i=1;i<=n;i++) {
            f[i][i]=0;s[i][i]=i;
        }
        for(int i=n-1;i>=1;i--)
            for(int j=i+1;j<=n;j++)
                for(int k=s[i][j-1];k<=s[i+1][j];k++)
                    if(f[i][j]>f[i][k]+f[k+1][j]+sum[j]-sum[i-1])
                    {
                        f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
                        s[i][j]=k;
                    }
                       
        cout<<f[1][n]<<endl;    
        return 0;
    }

    这样优化以后,复杂度更加接近于O(n2),科技解决接近n<3000的问题了~

    经典问题2:回文串

    回文串是正读反读都是一样的字符串,最经典的问题就是:给定一个字符串,通过增加或者删除得到一个回文串。

    poj 3280 给出一个由m中字母组成的长度为n的串,给出m种字母添加和删除花费的代价,求让给出的串变成回文串的代价。

    我们定义dp [ i ] [ j ] 为区间 i 到 j 变成回文的最小代价。

    那么有三种情况:

    首先:对于一个串如果s【i】==s【j】,那么dp【i】【j】=dp【i+1】【j-1】

    其次:如果dp【i+1】【j】是回文串,那么dp【i】【j】=dp【i+1】【j】+min(add【i】,del【i】);

    最后,如果dp【i】【j-1】是回文串,那么dp【i】【j】=dp【i】【j-1】 + min(add【j】,del【j】);

    参考代码:

    int main()
    {
        int n,m,t,i,j;
        while(~scanf("%d%d",&n,&m))
        {
            scanf("%s",s);
            for(i=0; i<n; i++)
            {
                scanf("%s",c);
                scanf("%d%d",&cost[c[0]-'a'],&t);
                if(t<cost[c[0]-'a'])
                    cost[c[0]-'a']=t;
            }
            memset(dp,0,sizeof(dp));
            for(i=m-1; i>=0; i--)
            {
                for(j=i+1; j<m; j++)
                {
                    dp[i][j]=min(dp[i+1][j]+cost[s[i]-'a'],dp[i][j-1]+cost[s[j]-'a']);
                    if(s[i]==s[j])
                        dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
                }
            }
            printf("%d
    ",dp[0][m-1]);
        }
        return 0;
    }

    例题:回文词

    Description
      回文词是一种对称的字符串——也就是说,一个回文词,从左到右读和从右到左读得到的结果是一样的。任意给定一个字符串,通过插入若干字符,都可以变成一个回文词。你的任务是写一个程序,求出将给定字符串变成回文词所需插入的最少字符数。比如字符串“Ab3bd”,在插入两个字符后可以变成一个回文词(“dAb3bAd”“Adb3bdA”)。然而,插入两个以下的字符无法使它变成一个回文词。
    Input
      第一行包含一个整数N,表示给定字符串的长度(3≤N≤50 00)。
      第二行是一个长度为N的字符串。字符串仅由大写字母“A”到“Z”,小写字母“a”到“z”和数字“0”到“9”构成。大写字母和小写字母将被认为是不同的。
    Output
      只有一行,包含一个整数,表示需要插入的最少字符数。
    Sample Input
    
    5
    Ab3bd
    Sample Output
    2
    回文词

    用字符串长度来减去(原字符串与反串)的最长公共子序列长度只能得到70分

    还是得用这种记忆化的dp才能AC:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=5005,inf=0x3f3f3f;; 
    int f[N][N],n,x;
    char s[N],rs[N]; 
    int dfs(int i,int j)
    {
        if (f[i][j]!=0xfffffff)
            return f[i][j];
        if (s[i-1]==s[j-1])
            return f[i][j]=dfs(i+1,j-1);
        else
            {  f[i][j]=min(dfs(i+1,j),dfs(i,j-1))+1;
            }
            return f[i][j];
    }
    int main()
    {  
        scanf("%d",&n);
        cin>>s;
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
            {
                f[i][j]=0xfffffff;f[i][i]=0;
            }
        f[1][n]=dfs(1,n);
        printf("%d",f[1][n]);
        return 0;
    }

    区间动态规划例题:(做到就来补一补)

     添加括号:

    Description
    【题目背景】
      给定一个正整数序列a1,a2,...,an,(1<=n<=20)
      不改变序列中每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和。
      例如:
      给出序列是4,123。
      第一种添括号方法:
        ((4+1)+(2+3))=((5)+(5))=(10)
      有三个中间和是5,510,它们之和为:5+5+10=20
      第二种添括号方法 
        (4+((1+2)+3))=(4+((3)+3))=(4+(6))=(10)
      中间和是3,610,它们之和为19。 
    【问题描述】 
      现在要添上n-1对括号,加法运算依括号顺序进行,得到n-1个中间和,求出使中间和之和最小的添括号方法。 
    Input
      共两行。
      第一行,为整数n。(1<=n<=20)
      第二行,为a1,a2,...,an这n个正整数,每个数字不超过100。
    Output
      输出3行。
      第一行,为添加括号的方法。
      第二行,为最终的中间和之和。
      第三行,为n-1个中间和,按照从里到外,从左到右的顺序输出。
    Sample Input
    4
    4 1 2 3
    Sample Output
    (4+((1+2)+3))
    19
    3 6 10
    添加括号

    主要是输出!!!其实计算最小值就跟合并石子差不多

    #include <bits/stdc++.h>
    using namespace std;
    const int N=110,inf=0x3f3f3f;
    int n,f[N][N],a[N],sum[N],loc[N][N];
    void print_equa(int l,int r)
    {
        if(l==r){ cout<<a[l];return; }
        cout<<"(";
        int k=loc[l][r];
        print_equa(l,k);
        cout<<"+";
        print_equa(k+1,r);
        cout<<")";
    }
    void print_sum(int l,int r)
    {
        if(!loc[l][r]) return;
        int k=loc[l][r];
        print_sum(l,k);
        print_sum(k+1,r);
        cout<<sum[r]-sum[l-1]<<" ";
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i]; 
            sum[i]=sum[i-1]+a[i];
        }
        memset(f,0x3f,sizeof(f));
        for(int i=1;i<=n;i++) f[i][i]=0;
        for(int t=2;t<=n;t++)
            for(int i=1;i<=n-t+1;i++)
            {
                int j=i+t-1;
                for(int k=i;k<j;k++)
                {
                    if(f[i][j]>f[i][k]+f[k+1][j]+sum[j]-sum[i-1])
                    {
                        f[i][j]=f[i][k]+f[k+1][j]+sum[j]-sum[i-1];
                        loc[i][j]=k;//记录两点之间的最佳位置 
                    }
                }
            }
        print_equa(1,n); 
        cout<<endl<<f[1][n]<<endl;
        print_sum(1,n);
    } 
    View Code

     能量项链:

    【题目描述】
    原题来自:NOIP 2006
    
    在 Mars 星球上,每个 Mars 人都随身佩带着一串能量项链。在项链上有 NN 颗能量珠。能量珠是一颗有头标记和尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记必定等于后一颗珠子的头标记。因为只有这样,通过吸盘——Mars 人吸收能量的器官的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可被吸盘吸收的能量。如果一颗能量珠头标记为 mm,尾标记为 rr,后一颗能量珠头标记为 rr,尾标记为 nn,则聚合后释放出 m×r×nm×r×n Mars单位的能量,新珠子头标记为 mm,尾标记为 nn。
    
    当需要时,Mars 人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不一样的。请设计一个聚合顺序使得一串珠子聚合后释放出的总能量最大。
    
    例如,设 N=4N=4,四颗珠子头标记与尾标记分别为 (2,3),(3,5),(5,10),(10,2)(2,3),(3,5),(5,10),(10,2)。我们用记号 ⨂⨂ 表示两颗珠子的聚合操作,(j⨂kj⨂k) 表示 j,kj,k 两颗珠子聚合后释放出的能量,则4,14,1两颗珠子聚合后所释放的能量为(41)=10×2×3=60(41)=10×2×3=60,这一串项链可以得到最优值的一个聚合顺序所释放出的总能量为(((41)⨂2)⨂3)=10×2×3+10×3×5+10×5×10=710(((41)⨂2)⨂3)=10×2×3+10×3×5+10×5×10=710
    现在给你一串项链,项链上有 nn 颗珠子,相邻两颗珠子可以合并成一个,合并同时会放出一定的能量,不同珠子合并放出能量不相同,请问按怎样的次序合并才能使得释放的能量最多?
    
    【输入】
    第一行一个正整数 nn
    第二行 nn 个不超过 10001000 的正整数,第 i(1≤i≤n)i(1≤i≤n) 个数为第 ii 颗珠子的头标记,当 i≠ni≠n 时第 ii 颗珠子的尾标记等于第 i+1i+1 颗珠子的头标记,当 i=ni=n 时第 ii 颗珠子的尾标记等于第 11 颗珠子的头标记。
    
    至于珠子的顺序,你可以这样确定:将项链放在桌面上,不要出现交叉,随机指定一颗珠子为第一颗珠子,按顺时针确定其它珠子的顺序。
    
    【输出】
    输出只有一行,一个不超过 2.1×1092.1×109 的正整数,表示最优聚合顺序所释放的能量。
    
    【输入样例】
    4
    2 3 5 10
    【输出样例】
    710
    【提示】
    数据范围与提示:
    
    对于 100% 的数据,4≤n≤1004≤n≤100
    能量项链

    和环形的石子合并题目十分相似

    #include <bits/stdc++.h>
    using namespace std;
    const int N=410,inf=0x3f3f3f;; 
    int tail[N],head[N];
    int f[N][N],n,x,maxx; 
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>head[i];
            head[i+n]=head[i];//
        }
        for(int i=1;i<=2*n-1;i++) tail[i]=head[i+1];
        tail[2*n]=head[1];
        for(int i=1;i<=2*n;i++) f[i][i]=0;//初始化 
        for(int t=1;t<=n-1;t++)
            for(int i=1;i<=2*n-t;i++)
            {
                int j=i+t;
                for(int k=i;k<=j-1;k++)
                {
                    f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+head[i]*tail[k]*tail[j]);
                }    
            }
        for(int i=1;i<=n;i++)
            maxx=max(maxx,f[i][i+n-1]);
        cout<<maxx<<endl;
        return 0;
    }
    View Code

     凸多边形的划分:

    难!的地方就是在所有的都是高精度计算

    【题目描述】
    给定一个具有 NN 个顶点的凸多边形,将顶点从 11 至 NN 标号,每个顶点的权值都是一个正整数。将这个凸多边形划分成 N−2N−2 个互不相交的三角形,试求这些三角形顶点的权值乘积和至少为多少。
    
    【输入】
    输入第一行为顶点数 NN
    第二行依次为顶点 11 至顶点 NN 的权值。
    
    【输出】
    输出仅一行,为这些三角形顶点的权值乘积和的最小值。
    
    【输入样例】
    5
    121 122 123 245 231
    【输出样例】
    12214884
    【提示】
    数据范围与提示:
    
    对于 100% 的数据,有 N≤50N≤50,每个点权值小于 109109
    凸多边形的划分
    #include <bits/stdc++.h>
    using namespace std;
    const int N=110,inf=0x3f3f3f;;
    /*思路跟前面一样,但是这个题需要高精度计算*/
    typedef long long ll;
    ll f[N][N][N],a[N],s1[N],s2[N],s3[N];
    int n;
    void Mark(ll c[])
    {
        for(int i=1;i<=c[0];i++)
        {
            c[i+1]+=c[i]/10000;
            c[i]%=10000;
        }
        while(c[c[0]+1])
        {
            c[0]++;
            c[c[0]+1]+=c[c[0]]/10000;
            c[c[0]]%=10000;
        }
    }
    void Add(ll a[],ll b[],ll c[])
    {
        if(a[0]>b[0]) c[0]=a[0];
        else c[0]=b[0];
        for(int i=1;i<=c[0];i++) c[i]=a[i]+b[i];
        Mark(c);
    }
    int Compare(ll a[],ll b[])
    {
        if(a[0]<b[0]) return 0;
        if(a[0]>b[0]) return 1;
        for(int i=a[0];i>=1;i--)
            if(a[i]<b[i]) return 0;
            else if(a[i]>b[i]) return 1;
        return 0;
    }
    void print()
    {
        cout<<f[1][n][f[1][n][0]];
        for(int i=f[1][n][0]-1;i>=1;i--)
        {
            cout<<f[1][n][i]/1000;
            cout<<f[1][n][i]/100%10;
            cout<<f[1][n][i]/10%10;
            cout<<f[1][n][i]%10;
        }
        cout<<endl;
    }
    void Mul(ll a1,ll a2,ll a3,ll c[])
    {
        c[0]=c[1]=1;
        for(int i=1;i<=c[0];i++) c[i]*=a1;
        Mark(c);
        for(int i=1;i<=c[0];i++) c[i]*=a2;
        Mark(c);
        for(int i=1;i<=c[0];i++) c[i]*=a3;
        Mark(c);
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                f[i][j][0]=0;//初始化
        for(int t=2;t<=n-1;t++)
            for(int i=1;i<=n-t;i++)
            {
                int j=i+t;
                f[i][j][0]=60;//这一步不懂? 
                for(int k=i+1;k<=j-1;k++){//枚举中间的位置 
                    memset(s1,0,sizeof(s1));
                    memset(s2,0,sizeof(s2));
                    memset(s3,0,sizeof(s3));
                    Mul(a[i],a[k],a[j],s1);//把三个数相乘放进s1中
                    Add(f[i][k],f[k][j],s2);//把这两个相加放进s2中
                    Add(s1,s2,s3);//把S1与s2加起来放s3中
                    if(Compare(f[i][j],s3)){
                        memcpy(f[i][j],s3,sizeof(s3));
                    } //就是f[i][j]=min(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k]); 
                } 
            } 
        print();
        return 0;
    }
    //其实最难的还是高精度的计算,大体的方向还是知道,但是一些小的细节的地方还是不是很清楚 
    View Code

     括号匹配:

    比较重要的思想就是一个算式:如果s[i]和s[j]相等的话,就有f[i][j]=f[i+1][j-1]+2;

    【题目描述】
    Hecy 又接了个新任务:BEBE 处理。BEBE 中有一类被称为 GBEGBE。
    
    以下是 GBEGBE 的定义:
    
    空表达式是 GBEGBE
    如果表达式 AA 是 GBEGBE,则 [A][A] 与 (A)(A) 都是 GBEGBE
    如果 AA 与 BB 都是 GBEGBE,那么 ABAB 是 GBEGBE。
    
    【输入】
    输入仅一行,为字符串 BEBE。
    
    【输出】
    输出仅一个整数,表示增加的最少字符数
    
    【输入样例】
    [])
    【输出样例】
    1
    【提示】
    数据范围与提示:
    
    对于 100% 的数据,输入的字符串长度小于 100100
    括号配对
    #include <bits/stdc++.h>
    using namespace std;
    const int N=110,inf=0x3f3f3f;
    int f[N][N];
    string s;
    int main()
    {
        cin>>s;
        for(int t=1;t<=s.size();t++)
            for(int i=0;i<=s.size()-t;i++)
            {
                int j=i+t;
                if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                    f[i][j]=f[i+1][j-1]+2;
                for(int k=i;k<j;k++)
                    f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
            }
        cout<<s.size()-f[0][s.size()-1]<<endl;
    }
    View Code

     分离与合体:

    这个题的其实也是典型的区间DP,唯一可能有区别的就是输出,是有次数的,哎,反正开始读题不是很理解这个输出。这是我自己最好理解的输出办法了

    【题目描述】
    经过在机房里数日的切磋,LYD 从杜神牛那里学会了分离与合体,出关前,杜神牛给了他一个测试……
    
    杜神牛造了 nn 个区域,他们紧邻着排成一行,编号 1..n1..n。在每个区域里都放着一把 OI 界的金钥匙,每一把都有一定的价值,LYD 当然想得到他们了。然而杜神牛规定 LYD 不能一下子把他们全部拿走,而是每次只可以拿一把。为了尽快得到所有金钥匙,LYD 自然就用上了刚学的分离与合体特技。
    
    一开始 LYD 可以选择 1..n−11..n−1 中的任何一个区域进入,我们不妨把这个区域记为 kk。进入后 LYD 会在 kk 区域发生分离,从而分离成两个小 LYD。分离完成的同时会有一面墙在 kk 区域和 k+1k+1 区域间升起,从而把 1..k1..k 和 k+1..nk+1..n 阻断成两个独立的区间,并在各自区间内任选除区间末尾之外(即从 1..k−11..k−1 和 k+1..n−1k+1..n−1中选取)的任意一个区域再次发生分离,这样就有了四个小小 LYD……重复以上所叙述的分离,直到每个小 LYD 发现自己所在的区间只剩下了一个区域,那么他们就可以抱起自己梦寐以求的 OI 金钥匙。
    
    但是 LYD 不能就分成这么多个个体存在于世界上,这些小 LYD 还会再合体,合体的小 LYD 所在区间中间的墙会消失。合体会获得 ((合并后所在区间左右端区域里金钥匙价值之和)×(之前分离的时候所在区域的金钥匙价值))。
    
    例如,LYD 曾在 1..31..3 区间中的 22 号区域分离成为 1..21..23..33..3 两个区间,合并时获得的价值就是 (( 11 号金钥匙价值 +3+3 号金钥匙价值)×( 22 号金钥匙价值))。
    
    LYD 请你编程求出最终可以获得的最大总价值,并按照分离阶段从前到后,区域从左到右的顺序,输出发生分离区域编号。若有多种方案,选择分离区域尽量靠左的方案(也可以理解为输出字典序最小的)。
    
    例如先打印一分为二的区域,然后从左到右打印二分为四的分离区域,然后是四分为八的……
    
    【输入】
    第一行一个正整数 nn
    
    第二行 nn 个用空格分开的正整数 aiai ,表示 1..n1..n 区域里每把金钥匙的价值。
    
    【输出】
    第一行一个数,表示获得的最大价值
    
    第二行按照分离阶段从前到后,区域从左到右的顺序,输出发生分离区域编号。若有多种方案,选择分离区域尽量靠左的方案(也可以理解为输出字典序最小的)。
    
    【输入样例】
    7
    1 2 3 4 5 6 7
    【输出样例】
    238
    1 2 3 4 5 6
    【提示】
    数据范围与提示:
    
    对于 20% 的数据,n≤10n≤10;
    
    对于 40% 的数据,n≤50n≤50;
    
    对于 100% 的数据,n,ai≤300n,ai≤300,保证运算过程和结果不超过 3232 位正整数范围。
    1573:分离与合体
    //yrnddup c++ code
    #include <bits/stdc++.h>
    using namespace std;
    const int N=305,inf=0x3f3f3f;
    int n;
    long long f[N][N],a[N],loc[N][N],h[N][N],o[N];
    void print(int l,int r,int count)
    {
        if(l==r) return;
        h[count][++o[count]]=loc[l][r];
        print(l,loc[l][r],count+1);
        print(loc[l][r]+1,r,count+1);
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        for(int t=1;t<n;t++)
            for(int i=1;i<=n-t;i++)
            {
                int j=i+t;
                for(int k=i;k<j;k++)
                    if(f[i][j]<f[i][k]+f[k+1][j]+(a[i]+a[j])*a[k])
                    {
                        f[i][j]=f[i][k]+f[k+1][j]+(a[i]+a[j])*a[k];
                        loc[i][j]=k;
                    }
            }
        cout<<f[1][n]<<endl;
        print(1,n,1);
        int i=1;
        while(o[i])
        {
            for(int j=1;j<=o[i];j++)
                cout<<h[i][j]<<" ";
            i++;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    围炉夜话(14)
    围炉夜话(13)
    ATmega128中应用的hex与bootloader的hex合并的实现
    配置IAR for AVR 既可以仿真也可以生成Hex文件
    atmega128 bootloader程序在IAR-AVR下 linker文件的配置及原因
    一步步学习SPD2010--第十四章节--在Web页面使用控件(8)--关键点
    一步步学习SPD2010--第十四章节--在Web页面使用控件(7)--使用SP服务器控件
    一步步学习SPD2010--第十四章节--在Web页面使用控件(6)--测试ASP.NET表单
    一步步学习SPD2010--第十四章节--在Web页面使用控件(4)--使用ASP.NET验证控件
    一步步学习SPD2010--第十四章节--在Web页面使用控件(3)--验证用户数据输入
  • 原文地址:https://www.cnblogs.com/sunny99/p/12644022.html
Copyright © 2011-2022 走看看