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 - 崭新龙狙,制霸全区 - 解题报告

  • 相关阅读:
    请求转发和请求重定向的区别
    查看电脑连过的WiFi密码
    linux mysql不能远程登录
    map的遍历方法
    ________________springbootのMybatis
    ________________springbootのTest
    ________________springbootの自定义starter
    ________________springbootのAOP
    ________________springbootのjdbc、事物
    ________________初学springboot14
  • 原文地址:https://www.cnblogs.com/dongdong25800/p/11145088.html
Copyright © 2011-2022 走看看