zoukankan      html  css  js  c++  java
  • 动态规划专题

     

     https://acm.uestc.edu.cn/contest/15/summary/?tdsourcetag=s_pctim_aiomsg

    dp专题要刷完!!

     A - oy环游世界 - 解题报告

    状态压缩dp入门题

    注意要开long long

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll dp[1<<18][18];
    ll e[20][20];
    ll x[20],y[20];
    const ll INF=1e18;
    int main()
    {
        int n,s;
        scanf("%d%d",&n,&s);
        int tot=0;
        for(int i=1; i<=n; i++)
        {
            ll a,b;
            scanf("%lld%lld",&a,&b);
            if(s==i)
            {
                x[n-1]=a;
                y[n-1]=b;
            }
            else
            {
                x[tot]=a;
                y[tot]=b;
                tot++;
            }
        }
        for(int i=0; i<tot; i++)
            for(int j=0; j<tot; j++)
            {
                e[i][j]=abs(x[i]-x[j])+abs(y[i]-y[j]);
            }
        for(int s=0;s<(1<<tot);s++)
            for(int i=0;i<tot;i++)
                dp[s][i]=INF;
        for(int i=0;i<tot;i++)
            dp[1<<i][i]=abs(x[i]-x[n-1])+abs(y[i]-y[n-1]);
        for(int s=0;s<(1<<tot);s++)
            for(int i=0;i<tot;i++)
                for(int j=0;j<tot;j++)
                {
                    if(i!=j&&((1<<i)&s)&&(((1<<j)&s)==0))
                    {
                        dp[s|1<<j][j]=min(dp[s|1<<j][j],dp[s][i]+e[i][j]);
                    }
                }
        ll ans=INF;
        for(int i=0;i<tot;i++)
        {
            ans=min(ans,dp[(1<<tot)-1][i]);
    //        printf("%d
    ",dp[(1<<tot)-1][i]);
        }
        printf("%lld",ans);
    }
    View Code

    B - 挖矿攻略 - 解题报告

    棋盘dp问题

    日哦,没说矿道不能拐弯?只能直西直北?浪费时间!

    #include<bits/stdc++.h>
    using namespace std;
    
    int a[505][505];
    int b[505][505];
    int xi[505][505];
    int bei[505][505];
    int f[505][505];
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)&&n)
        {
            memset(f,0,sizeof(f));
            memset(xi,0,sizeof(xi));
            memset(bei,0,sizeof(bei));
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++)
                {
                    scanf("%d",&a[i][j]);
                    xi[i][j]+=xi[i][j-1]+a[i][j];
                }
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++)
                {
                    scanf("%d",&b[i][j]);
                    bei[i][j]+=bei[i-1][j]+b[i][j];
                }
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++)
                    f[i][j]=max(xi[i][j]+f[i-1][j],bei[i][j]+f[i][j-1]);
            printf("%d
    ",f[n][m]);
        }
    }
    View Code

    C - 手办 - 解题报告

    四位数组DP好题,找了好久BUG,这道题要优化,时间空间上都需要。

    思路:状态f[i][j][k][l],考虑了i个物品,取了j次,状压当前书架上剩余的高度状态k,此时书架最后一本书高度j,的最小混乱度。

    状态转移:

    a[i]==l,一定不取,f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k][l])

    a[i]!=l,取第i个物品f[i][j+1][k][l]=f[i-1][j][k][l],不取的话f[i][j][新的k][a[i]]=f[i-1][j][k][a[i]]+1

    之后枚举i,j,k,l得到的混乱度加上该状态下未出现的高度,求最小值。

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x3f3f3f3f;
    int f[2][105][1<<8+1][9];// 如果第一维105会达到1e9超内存,需要滚动数组优化
    int a[105];
    int v[10];
    int num[1<<9+10];
    
    int init(int x)
    {
        int ret = 0;
        while(x)
        {
            if(x&1)
                ret++;
            x >>= 1;
        }
        return ret;
    }
    
    int main()
    {
        int it=0;
        int n,m,now;
        for (int i = 0; i <= 1<<8; i++)
            num[i] = init(i);
        while(~scanf("%d%d",&n,&m)&&n+m)
        {
            int tot=0; // tot记录所有出现的高度个数 
            int all=0; // all这里用来记录出现的最大状态 
            memset(v,0,sizeof(v));
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&a[i]);
                a[i]-=114514;
                all|=(1<<a[i]);
                if(v[a[i]]==0)
                {
                    tot++;
                    v[a[i]]=1;
                }
            }
            memset(f[0],INF,sizeof(f[0]));
            f[0][0][0][8]=0;
            for(int i=1; i<=n; i++)
            {
                now=i%2;
                memset(f[now],INF,sizeof(f[now]));
                for(int j=0; j<=min(m,i); j++)
                    for(int s=0; s<=all; s++)
                        for(int k=0; k<=8; k++)
                        {
                            if(f[!now][j][s][k]==INF) continue;
                            if(k==a[i]) f[now][j][s][k]=min(f[now][j][s][k],f[!now][j][s][k]);
                            else
                            {
                                f[now][j][s|(1<<a[i])][a[i]]=min(f[now][j][s|(1<<a[i])][a[i]],f[!now][j][s][k]+1);
                                f[now][j+1][s][k]=min(f[now][j+1][s][k],f[!now][j][s][k]);
                            }
                        }
            }
            int ans=INF;
            for(int j=0; j<=m; j++)
                for(int s=0; s<=all; s++)
                    for(int k=0; k<=8; k++)
                    {
                        if(f[now][j][s][k]!=INF)
                            ans=min(ans,f[now][j][s][k]+tot-num[s]); // 所有出现的高度tot-状态含的num[s]就是未出现的高度 
                    }
            printf("Case %d: %d
    
    ",++it,ans);
        }
    }
    View Code

    D - 序列 - 解题报告

    最长上升路径nlogn做法

    正反求一次到每个数字且末尾数字为该数字的最长上升子序列,然后枚举每个端点作为中间值。注意!初始化box为负无穷,注意数据范围

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e6+10;
    #define ll long long
    int lt[maxn],rt[maxn];
    int box[maxn];
    int a[maxn];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        int len=0;
        box[0]=-1e9-10;
        for(int i=1; i<=n; i++)
        {
            if(a[i]>box[len])
            {
                box[++len]=a[i];
                lt[i]=len;
            }
            else
            {
                int pos=lower_bound(box+1,box+1+len,a[i])-box;
                box[pos]=a[i];
                lt[i]=pos;
            }
    
        }
        len=0;
        memset(box,0,sizeof(box));
        box[0]=-1e9-10;
        for(int i=n; i>=1; i--)
        {
            if(a[i]>box[len])
            {
                box[++len]=a[i];
                rt[i]=len;
            }
            else
            {
                int pos=lower_bound(box+1,box+1+len,a[i])-box;
                box[pos]=a[i];
                rt[i]=pos;
            }
        }
        ll ans=0;
        for(int i=1; i<=n; i++)
            ans=max(ans,1ll*min(lt[i],rt[i])*2-1);
        printf("%lld",ans);
    }
    View Code

    E - 神 - 解题报告

    线性DP,设f[i][j]表示,到第i块中j的位置作为最后一个字符的最小划分数。

    状态转移:枚举i块所有位置j作为最后一个位置,再枚举i-1块中所有位置k作为最后一个位置,如果i-1块中字母在i中出现,同时满足s[j]!=s[k]或者如果s[j]==s[k]&&cnt=1,cnt是i块中字母种类数,那么这个i的块数产生的划分数可以为cnt-1,否则只能是cnt。

    #include<bits/stdc++.h>
    using namespace std;
    
    int f[1005][1005];
    char s[1005];
    int v[30];
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            int k;
            scanf("%d%s",&k,s+1);
            int len=strlen(s+1);
            int duan=len/k;
            int cnt=0;
            memset(v,0,sizeof(v));
            for(int i=1; i<=k; i++)
            {
                if(v[s[i]-'a'+1]==0)
                    cnt++;
                v[s[i]-'a'+1]=1;
            }
            memset(f,0x3f,sizeof(f));
            for(int i=1; i<=k; i++)
                f[1][i]=cnt;
            for(int i=2; i<=duan; i++)
            {
                memset(v,0,sizeof(v));
                cnt=0;
                for(int j=(i-1)*k+1; j<=i*k; j++)
                {
                    if(v[s[j]-'a'+1]==0)
                        cnt++;
                    v[s[j]-'a'+1]=1;
                }
                for(int j=1; j<=k; j++)
                {
                    for(int kk=1; kk<=k; kk++)
                    {
                        int jt=(i-1)*k+j;
                        int jk=(i-2)*k+kk;
                        if(v[s[jk]-'a'+1]==1&&(cnt==1||s[jt]!=s[jk]))
                            f[i][j]=min(f[i][j],f[i-1][kk]+cnt-1);
                        else
                            f[i][j]=min(f[i][j],f[i-1][kk]+cnt);
                    }
                }
    //            for(int j=1;j<=k;j++)
    //                printf("%d ",f[i][j]);
    //            printf("
    ");
            }
            int ans=0x3f3f3f3f;
            for(int i=1;i<=k;i++)
                ans=min(ans,f[duan][i]);
            printf("%d
    ",ans);
        }
    }
    View Code

    F - 苇名欧一郎 - 解题报告

    状压dp,做完这道题感觉对状压的DP又加深了理解。

    思路:f[1<<16]状压杀死的人,与A题类似,每个状态下枚举可以杀的敌人,如果可以f[t]+=f[s]s为目前状态,t为加了这个可以杀的人的状态。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll f[1<<16];
    char a[20][20];
    
    int main()
    {
        int t,n;
        scanf("%d",&t);
        for(int it=1; it<=t; it++)
        {
            scanf("%d",&n);
            scanf("%s",a[n]);
            for(int i=0; i<=n-1; i++)
                scanf("%s",a[i]);
            memset(f,0,sizeof(f));
            f[0]=1;
            for(int s=0; s<1<<n; s++)
            {
                for(int i=0; i<n; i++)
                {
                    if((s&(1<<i))==0)
                    {
                        int flag=0;
                        if(a[n][i]=='1') flag=1;
                        for(int j=0; j<n; j++)
                        {
                            if(s&(1<<j)&&a[j][i]=='1')
                                flag=1;
                            if(flag) break;
                        }
                        if(flag) f[s|1<<i]+=f[s];
                    }
                }
            }
            printf("Case %d: %lld
    ",it,f[(1<<n)-1]);
        }
    }
    View Code

    G - 子串 - 解题报告

    这道题需要降维或者滚动数组优化,我选择了滚动数组,这样比较好理解好写hh

    思路:设f[i][j],以第i位为最低位的字符串%k余j的个数。这里需要注意只能从高位往低位滚动,这样余数*10就是加上新的数的余数,而如果从低位往高位就搞不定了呀。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e7+10;
    #define ll long long
    ll f[2][maxn];
    char a[maxn];
    int main()
    {
        int n,k;
        scanf("%d%d",&n,&k);
        scanf("%s",a+1);
        int now;
        ll ans=0;
        for(int i=1; i<=n; i++)
        {
            now=i%2;
            for(int j=0;j<=k;j++)
                f[now][j]=0;
            int t=(a[i]-'0');
            for(int j=0; j<=k; j++)
            {
                int tt=(t+j*10)%k;
                f[now][tt]+=f[!now][j];
            }
            t=t%k;
            f[now][t]++;
            ans+=f[now][0];
        }
        printf("%lld",ans);
    }
    View Code

    H - van游戏 - 解题报告

    I - 攻略妹纸 - 解题报告

    变形的01背包问题

    思路:一开始可以想到,可以枚举自身的上限魅力值,也就是给妹子的最大花费,之后就是一个01背包O(N3),但可以想到如果我们给妹子所需最大魅力值从大到小排序,以每个妹子的所需魅力值作为最大魅力值花费的上限来枚举,同时这样排过序之后,不同魅力值之间的f[][]是不会相互覆盖的,可以优化为O(N2)。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=6005;
    #define ll long long
    
    ll f[maxn];
    ll n,m,k,x,y,ans;
    ll a,b,c,d;
    struct note
    {
        ll val,cost,goal;
    } g[maxn];
    int cmp(note a,note b)
    {
        return a.goal>b.goal;
    }
    int main()
    {
        scanf("%lld%lld%lld%lld%lld",&n,&m,&k,&x,&y); // x氪自己 y氪妹子
        for(int i=1; i<=n; i++)
        {
            scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
            g[i].val=a;
            // 你对她好感a,她对你好感b,c妹子对你好感要求c,你魅力要求d
            if(k<d)
                g[i].goal=m-(d-k)*x;
            else
                g[i].goal=m;
            if(c>b)
                g[i].cost=(c-b)*y;
            else
                g[i].cost=0;
        }
        sort(g+1,g+1+n,cmp);
        for(int i=1; i<=n; i++)
        {
            for(int j=g[i].goal; j>=g[i].cost; j--)
                f[j]=max(f[j],f[j-g[i].cost]+g[i].val);
            ans=max(ans,f[g[i].goal]);
        }
        printf("%lld",ans);
    }
    View Code

    K - 抽卡 - 解题报告

    L - zh的奖杯 - 解题报告

    M - 崭新龙狙,制霸全区 - 解题报告

  • 相关阅读:
    基于Metaweblog API 接口一键发布到国内外主流博客平台
    uva144 Student Grants
    Uva 10452
    Uva 439 Knight Moves
    Uva 352 The Seasonal War
    switch语句
    java——基础知识
    我的lua学习2
    codeforces 431 D. Random Task 组合数学
    codeforces 285 D. Permutation Sum 状压 dfs打表
  • 原文地址:https://www.cnblogs.com/dongdong25800/p/11145088.html
Copyright © 2011-2022 走看看