zoukankan      html  css  js  c++  java
  • 动态规划自虐行为刷题——

    P1220 关路灯 P2279 [HNOI2003]消防局的设立 P1373 小a和uim之大逃离 P1005 矩阵取数游戏

    先说关路灯吧,几个世纪不打区间DP都忘了;

    一个老人来回关灯,求最小消耗功率

    主要思路是从起点(家)拓展区间,由小区间转移到大区间,因为有继续向前走还有回头关灯两种选择

    我们设f[i][j][k]表示区间i到j已经关完,k=0表示现在老人停在i节点上,k=1表示停在j节点上。

    前缀和求两侧没有关的灯的功率;

    f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));

    ij关掉,由i+1,j在左在右转移;

    既然都关完停在了i节点,一定是i没有关,而i+1到j都已经关上了;

    实际上转移过程像一滴水摊开一样

    f[i][j][1]同理

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=60;
    int n,c;
    int a[maxn],b[maxn],sum[maxn];
    int f[maxn][maxn][2];
    
    int main()
    {
        memset(f,127,sizeof(f));
        scanf("%d%d",&n,&c);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            sum[i]=sum[i-1]+b[i];
        }
        f[c][c][0]=0;f[c][c][1]=0;
        for(int l=2;l<=n;l++)
        {
            for(int i=1;i+l-1<=n;i++)
            {
                int j=i+l-1;
                f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));
                f[i][j][1]=min(f[i][j-1][1]+(a[j]-a[j-1])*(sum[i-1]+sum[n]-sum[j-1]),f[i][j-1][0]+(a[j]-a[i])*(sum[i-1]+sum[n]-sum[j-1]));
            }
        }
        printf("%d",min(f[1][n][1],f[1][n][0]));
        return 0;
    }

    再来说一说消防局的设立,这道题的(题解)倒是给了一个很好地思路(我也不会做,只能看题解了)

    一个贪心的思路,就是吗,面对一棵树,在一定范围内定点能覆盖更多的点,且答案不会更差

    题的意思是找到最少的点覆盖整个图(不然家都烧没了消防队还没到)
    面对这个距离2,我们可以通过一个点,一个点的父亲,还有他的爷爷来更新,当然要从最底层一直更新到整个树的根节点

    设disifre[i]表示距离i节点最近的消防站距离是多少,如果大于2,就在爷爷的位置安放一个消防站,然后更新爷爷的父亲和爷爷的爷爷(脑子混乱)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2010;
    int n;
    int f[maxn];
    int disfire[maxn];
    int dis[maxn];
    int id[maxn]; 
    bool cmp(int x,int y){return dis[x]>dis[y];}
    int ans;
    int main()
    {
        //freopen("input.txt","r",stdin);
    //    freopen("output.txt","w",stdout);
        memset(disfire,127,sizeof(disfire));
        scanf("%d",&n);
        id[1]=1;
        for(int i=2;i<=n;i++)
        {
            scanf("%d",&f[i]);
            dis[i]=dis[f[i]]+1;
            id[i]=i;
        }
        sort(id+1,id+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            int x=id[i],fa=f[x],gra=f[fa];
            disfire[x]=min(disfire[x],min(disfire[fa]+1,disfire[gra]+2));
            if(disfire[x]>2)
            {
                disfire[gra]=0;ans++;
                disfire[f[gra]]=min(disfire[f[gra]],1);
                disfire[f[f[gra]]]=min(disfire[f[f[gra]]],2);
            }
        }
        printf("%d",ans);
        return 0;
    }

    连图都没建,就把一道树形DP做出来了(开心)

    但是我们只有这些还不够,我们可以拓展到距离为k

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    #include<queue>
    using namespace std;
    const int maxn=2010;
    int pre[maxn],last[maxn],other[maxn],l;
    queue<int>q;
    stack<int> s;
    int n;
    int fa[maxn];
    void add(int x,int y)
    {
        l++;
        pre[l]=last[x];
        last[x]=l;
        other[l]=y;
    }
    int vis[maxn];
    void bfs()
    {
        q.push(1);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            if(vis[x]) continue;
            vis[x]=1;
            s.push(x);//保证深度小的先入站 
            for(int p=last[x];p;p=pre[p])
            {
                int v=other[p];
                if(vis[v]) continue;
                fa[v]=x;
                q.push(v);
            }
        }
    }
    int ans;
    
    void dfs(int x,int dis)
    {
        if(dis>2) return ;
        vis[x]=1;
        for(int p=last[x];p;p=pre[p])
        {
            int v=other[p];
            dfs(v,dis+1);//更新访问的点,已经有消防站覆盖 
        }
    }
    
    void go()
    {
        memset(vis,0,sizeof(vis));
        while(!s.empty())
        {
            int x=s.top();
            s.pop();
            if(vis[x]) continue;
            ans++;dfs(fa[fa[x]],0);
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        {
            int ui;
            scanf("%d",&ui);
            add(i,ui);
            add(ui,i);
        }
        bfs();
        go();
        printf("%d",ans);
        return 0;0
    }

    至于大逃离,我也不知道为什么能这么做。。。

    f[i][j][p][k],位置i,j,收集魔液差为p,k=0表示小a,k=1表示uim

    因为k+1变成0,所以k++;

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int mo=1e9+7;
    const int maxn=810;
    int f[maxn][maxn][20][2];
    int n,m,k;
    int ans;
    int g[maxn][maxn];
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        k++;
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
        {
            scanf("%d",&g[i][j]);
            f[i][j][g[i][j]][0]=1;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                for(int p=0;p<=k;p++)
                {
                f[i][j][p][0]+=f[i-1][j][(p-g[i][j]+k)%k][1];
                    f[i][j][p][0]%=mo;
                f[i][j][p][0]+=f[i][j-1][(p-g[i][j]+k)%k][1];
                    f[i][j][p][0]%=mo;
                f[i][j][p][1]+=f[i-1][j][(p+g[i][j]+k)%k][0];
                    f[i][j][p][1]%=mo;
                f[i][j][p][1]+=f[i][j-1][(p+g[i][j]+k)%k][0];
                    f[i][j][p][1]%=mo;
                }
                ans+=f[i][j][0][1];
                ans%=mo;
            }
        }
        printf("%d",ans);
        return 0;
    }

    矩阵取数,每次都取每行的前一个或最后一个,每行互不影响,所以一行一行做就行了

    因为我也不知道在哪里结束的,所以就打个擂台吧

    #include<bits/stdc++.h>
    #define ll __int128
    using namespace std;
    inline int read() 
    {
        int X=0,w=1;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
        return X*w;
    }
    
    ll ans;
    ll n,m;
    ll a[100][100];
    ll base[100];
    ll f[100][100];
    
    void print(ll x)
    {
        if(!x) return;
        if(x) print(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
        n=read();m=read();
        base[0]=1;
        for(int i=1;i<=m;i++)
        {
            base[i]=base[i-1]*2;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                a[i][j]=read();
            }
        }
        for(int k=1;k<=n;k++)
        {
            memset(f,0,sizeof(f));
            ll sum=0;
            for(int l=1;l<=m;l++)
            {
                for(int r=m;r>=1;r--)
                {
                    f[l][r]=max(f[l][r],f[l-1][r]+base[m-(r-l+1)]*a[k][l-1]);
                    f[l][r]=max(f[l][r],f[l][r+1]+base[m-(r-l+1)]*a[k][r+1]);
                    sum=max(sum,f[l][r]);
                }
            }
            ans+=sum;
        }
        if(!ans) printf("0");
        else print(ans);
        return 0; 
    }
  • 相关阅读:
    蓝桥杯--算法训练 区间k大数查询
    vijos1782:借教室
    vijos1779国王游戏
    C++大数模板
    HDU1042(N!:设4为基数)
    HDU1026(延时迷宫:BFS+优先队列)
    POJ3984(迷宫问题)
    HDU3018:Ant Trip(欧拉回路)
    HDU5438:Ponds(拓扑排序)
    2008北航:字符串匹配
  • 原文地址:https://www.cnblogs.com/WHFF521/p/11508486.html
Copyright © 2011-2022 走看看