zoukankan      html  css  js  c++  java
  • 2017-3-10校内训练

    hzwer又在虐人 照常ACM赛制 AC:5/7

    B.复杂的整数划分问题

    题目大意:将正整数n表示成一系列正整数之和,n=n1+n2+…+nk,其中n1>=n2>=…>=nk>=1,k>=1。正整数n的这种表示称为正整数n的划分。多组询问,每次给出N和K,要求回答:N划分成K个正整数之和的划分数目,N划分成若干个不同正整数之和的划分数目,N划分成若干个奇正整数之和的划分数目。(0<k<=n<=50)

    思路:先N^3随便DP一下即可O(1)回答询问(应该有复杂度更低的做法,不过毕竟这题N比较小)。问题1:f[i][j][k]表示和为i,分出j个,最大不超过k的方案数;问题2:f[i][j]表示和为i,最大为j的方案数,问题3同问题2。转移方程比较容易……

    #include<cstdio>
    #define MN 50
    int f1[MN+5][MN+5][MN+5],f2[MN+5][MN+5],f3[MN+5][MN+5];
    int main()
    {
        int i,j,k,n;
        for(i=1;i<=MN;++i)f1[0][0][i]=1;
        for(i=1;i<=MN;++i)for(j=1;j<=MN;++j)for(k=1;k<=MN;++k)
            f1[i][j][k]=f1[i][j][k-1]+(i>=k?f1[i-k][j-1][k]:0);
        f2[0][0]=f3[0][0]=1;
        for(i=1;i<=MN;++i)for(j=1;j<=i;++j)for(k=0;k<j;++k)f2[i][j]+=f2[i-j][k];
        for(i=1;i<=MN;++i)for(j=1;j<=i;j+=2)for(k=0;k<=j;++k)f3[i][j]+=f3[i-j][k];
        for(i=1;i<=MN;++i)for(j=1;j<=MN;++j)f2[i][j]+=f2[i][j-1],f3[i][j]+=f3[i][j-1];
        while(~scanf("%d%d",&n,&k))printf("%d
    %d
    %d
    ",f1[n][k][MN],f2[n][MN],f3[n][MN]);
    }

    C.棋盘分割

    题目大意:给定一个8*8的数字矩阵,每次可以从大矩阵切一刀切成两个小矩阵,选择其中一个小矩阵继续切,最后分成n个小矩阵,用xi表示第i个小矩阵内的元素和,要求最小化xi的均方差。(均方差:令xi的平均数为x,均方差=((xi-x)^2/n)^0.5)(n<15)

    思路:由于n给定,题目所求即最小化(xi-x)^2,事实上我们发现,xi之和即为整个矩阵的和,也就是说,给定n后,平均数x是个定值,我们预处理出二维前缀和即可知道每个子矩阵对答案的贡献,用f[p][i][j][k][l]表示用p个子矩形拼成以(i,j)为左上角,(k,l)为右下角的矩形,直接DP就可以了,复杂度O(n*8^5)。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    double f[15][10][10][10][10];
    int a[10][10];
    inline double sqr(double x){return x*x;}
    int main()
    {
        int n,i,j,k,l,p,d;double b;
        scanf("%d",&n);
        for(i=1;i<=8;++i)for(j=1;j<=8;++j)
        {
            scanf("%d",&k);
            a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+k;
        }
        b=(double)a[8][8]/n;
        for(p=1;p<=n;++p)for(i=0;i<10;++i)for(j=0;j<10;++j)
            for(k=0;k<10;++k)for(l=0;l<10;++l)f[p][i][j][k][l]=0x7FFFFFFF;
        for(i=1;i<=8;++i)for(j=1;j<=8;++j)for(k=i;k<=8;++k)for(l=j;l<=8;++l)
            f[1][i][j][k][l]=sqr(a[k][l]-a[i-1][l]-a[k][j-1]+a[i-1][j-1]-b);
        for(p=2;p<=n;++p)for(i=1;i<=8;++i)for(j=1;j<=8;++j)for(k=i;k<=8;++k)for(l=j;l<=8;++l)
        {
            for(d=i;d<k;++d)f[p][i][j][k][l]=min(f[p][i][j][k][l],f[p-1][i][j][d][l]+f[1][d+1][j][k][l]);
            for(d=k;d>i;--d)f[p][i][j][k][l]=min(f[p][i][j][k][l],f[p-1][d][j][k][l]+f[1][i][j][d-1][l]);
            for(d=j;d<l;++d)f[p][i][j][k][l]=min(f[p][i][j][k][l],f[p-1][i][j][k][d]+f[1][i][d+1][k][l]);
            for(d=l;d>j;--d)f[p][i][j][k][l]=min(f[p][i][j][k][l],f[p-1][i][d][k][l]+f[1][i][j][k][d-1]);
        }
        printf("%.3lf",sqrt(f[n][1][1][8][8]/n));
    }

    D.ROADS

    题目大意:给定一张N个点M条边的有向图,边有长度和费用,求从点1走到点N,费用不超过K的最短路。(N<=100,M<=10000,K<=10000,0<=边费用<=100,1<=边长<=100)

    思路:观察发现边长和费用的范围都比较小,考虑DP,由于边的费用可能为0不好处理,我们用边长表示状态来DP,f[i][j]表示走到点j共走了i的最小花费,直接DP即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 100
    #define ML 10000
    #define ME 10000
    #define INF 1000000000
    int h[MN+5],rh[MN+5],en,f[ML+5][MN+5],q[MN+5],qn,u[MN+5];
    struct edge{int nx,t,w,c;}e[ME*2+5];
    inline void ins(int*h,int x,int y,int w,int c){e[++en]=(edge){h[x],y,w,c};h[x]=en;}
    int main()
    {
        int K,n,m,x,y,w,i,j,k;
        K=read();n=read();m=read();
        while(m--)x=read(),y=read(),w=read(),ins(h,x,y,w,read()),ins(rh,y,x,0,0);
        for(u[q[i=0]=n]=1;i<=qn;++i)for(j=rh[q[i]];j;j=e[j].nx)if(!u[e[j].t])u[q[++qn]=e[j].t]=1;
        if(!u[1])return puts("-1"),0;
        memset(f,127,sizeof(f));f[0][1]=0;
        for(i=0;f[i][n]>K&&i<=ML;++i)for(j=1;j<=n;++j)if(f[i][j]<INF)for(k=h[j];k;k=e[k].nx)if(u[e[k].t])
            if(i+e[k].w<=ML)f[i+e[k].w][e[k].t]=min(f[i+e[k].w][e[k].t],f[i][j]+e[k].c);
        for(i=0;i<=ML;++i)if(f[i][n]<=K)break;
        printf("%d",i<=ML?i:-1);
    }

    E.A decorative fence

    题目大意:求字典序第K大的满足a1<a2,a3>a2,a4<a3……或a1>a2,a3<a2,a4>a3……的1~N排列。(N<=20,多组询问)

    思路:DP求出f[i][j]表示1~i的排列,第1个为j,满足其中一个性质的方案数,满足另一个性质的方案数即为f[i][i-j+1](全部倒过来)。求f[i]时我们可以视作从1~i的排列删掉j,原本大于j的都减1,就变成一个1~i-1的排列,就可以从f[i-1]转移。求第k大我们枚举每一位取什么,利用DP数组计算方案数,做法与DP过程类似。复杂度O(n^3+Tn^2)(理论上应该还可以更低)。

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define MN 20
    ll f[MN+5][MN+5];
    int ans[MN+5];
    int main()
    {
        int T,i,j,k,n,v;ll c;
        for(f[1][1]=1,i=2;i<=MN;++i)for(j=2;j<=i;++j)for(k=1;k<j;++k)f[i][j]+=f[i-1][i-k];
        for(scanf("%d",&T);T--;)
        {
            cin>>n>>c;
            for(i=1;i<=n;++i)
                if(f[n][i]+f[n][n-i+1]>=c)break;
                else c-=f[n][i]+f[n][n-i+1];
            ans[0]=i;
            if(n>1)
            {
                for(i=1;i<ans[0];++i)
                    if(f[n-1][n-i]>=c)break;
                    else c-=f[n-1][n-i];
                if(v=i==ans[0])for(;i<n;++i)
                    if(f[n-1][i]>=c)break;
                    else c-=f[n-1][i];
                ans[1]=i;
            }
            for(i=2;i<n;++i,v^=1)
            {
                if(v)for(j=1;j<ans[i-1];++j)
                    if(f[n-i][n-i-j+1]>=c)break;
                    else c-=f[n-i][n-i-j+1];
                else for(j=ans[i-1];j<=n-i;++j)
                    if(f[n-i][j]>=c)break;
                    else c-=f[n-i][j];
                ans[i]=j;
            }
            for(i=n;i--;)for(j=i+1;j<n;++j)if(ans[j]>=ans[i])++ans[j];
            for(i=0;i<n;++i)printf("%d ",ans[i]);puts("");
        }
    }

    G.单词

    题目大意:一篇由n个单词组成的文章,问每个单词在整篇文章中出现多少次(被包含在其他单词内也算)。(n<=200,总长不超过10^6)

    思路:比较简单的AC自动机。建出Trie树后我们可以知道每个单词作为其他单词前缀的出现次数,建出fail树再DP后我们即可知道每个单词作为其他单词子串的出现次数。复杂度O(总长)。

    #include<iostream>
    #define ll long long
    using namespace std;
    #define MN 200
    #define ML 1000000
    string s[MN+5];
    int c[ML+5][26],f[ML+5],tn,q[ML+5],qn,p[MN+5];
    ll v[ML+5];
    void build()
    {
        int i,j,k;
        for(i=0;i<26;++i)if(c[0][i])q[++qn]=c[0][i];
        for(i=1;i<=qn;++i)for(j=0;j<26;++j)if(c[q[i]][j])
        {
            for(k=f[q[i]];k&&!c[k][j];k=f[k]);
            f[q[++qn]=c[q[i]][j]]=c[k][j];
        }
    }
    int main()
    {
        int n,i,j,k;
        cin>>n;
        for(i=1;i<=n;++i)
        {
            cin>>s[i];
            for(j=k=0;s[i][j];++j)
            {
                if(!c[k][s[i][j]-='a'])c[k][s[i][j]]=++tn;
                ++v[k=c[k][s[i][j]]];
            }
            p[i]=k;
        }
        build();
        for(i=qn;i;--i)v[f[q[i]]]+=v[q[i]];
        for(i=1;i<=n;++i)cout<<v[p[i]]<<endl;
    }
  • 相关阅读:
    应用实例-最大子列和问题
    什么是算法
    什么是数据结构
    数据结构与算法-Python/C(目录)
    collections模块
    色彩缤纷的Python(改变字体颜色及样式)
    取数组中一段值的算法(转)
    开发者必备,超实用的PHP代码片段(转)
    php中数组自定义排序
    rand值出现负数的解决方案
  • 原文地址:https://www.cnblogs.com/ditoly/p/20170310C.html
Copyright © 2011-2022 走看看