• topcoder srm 707 div1


    1 构造一个棋盘,长宽n,m不超过50,每个格子为障碍或者非障碍两种,使得从(0,0)到(n-1,m-1)的最短路为给定的值k。

    思路:如果k小于等于98,那么一定存在没有障碍的棋盘满足要求。否则,最后的路径可以如下图所示(白色为障碍)。假设一开始向左弯曲到最左侧的次数为x,最后一次向左弯曲的长度为y,然后枚举矩形的长宽,判断是否等于k即可。

    2 将s变成t,每次操作可以将s加上a,或者将s乘上b。问最少的操作次数。

    思路:b为0或者1是单独讨论。现在假设b大于1.设一共经过了m次乘操作,第i次乘法操作前进行的加法操作次数为$p_{i}$,那么最后得到的值为$((((s+p_{0}a)b+p_{1}a)b+...)b+p_{m-1}a)b+p_{m}$$=sb^{m}+asum_{i=0}^{m}p_{i}b_{m-i}$。所以首先枚举m,那么$sum_{i=0}^{m}p_{i}b_{m-i}=frac{t-sb^{m}}{a}$。这时候贪心即可确定各个$p_{i}$,答案为$m+sum_{i=0}^{m}p_{i}$

    void up(long long &x,long long y)
    {
        if(x==-1||x>y) x=y;
    }
    
    class MultiplyAddPuzzle {
    public:
        long long minimalSteps(long long s, long long t, long long a, long long b)
        {
            if(s==t) return 0;
            if(b==0)
            {
                if(t==0) return 1;
                if(a==0) return -1;
                if(t>s&&(t-s)%a==0) return (t-s)/a;
                if(t%a==0) return t/a+1;
                return -1;
            }
            if(b==1)
            {
                if(a==0) return -1;
                if(t>s&&(t-s)%a==0) return (t-s)/a;
                return -1;
            }
    
            long long p[63];
            p[0]=1;
            for(int i=1;i<63;++i) p[i]=p[i-1]*b;
            long long ans=-1;
    
            for(int m=0;m<62;++m)
            {
                if(a==0)
                {
                    if(t%p[m]==0&&t/p[m]==s) up(ans,m);
                }
                else
                {
                    if(t/p[m]<s) break;
                    if((t-p[m]*s)%a==0)
                    {
                        long long S=(t-p[m]*s)/a;
                        long long curAns=m;
                        for(int i=m;i>=0;--i)
                        {
                            curAns+=S/p[i];
                            S-=S/p[i]*p[i];
                        }
                        up(ans,curAns);
                    }
                }
                if(b>t/p[m]) break;
            }
            return ans;
        }
    };
    

     3 有一个棋盘,棋盘的某些位置上放有棋子。有依次执行的若干操作,每个操作是将其中的一个棋子移动a到相邻的某个格子b上。每次移动时,要求a上必须有一个棋子,b上没有棋子。对于一个操作序列,若存在某个初始的棋盘使得该操作序列的每个操作都合法,那么称该操作序列合法。求最少删掉操作序列的多少个操作可以使得上下的序列合法。

    思路: $u$移动到$v$,增加三条边,如下图所示。所有边流量均为1,代价除了-1外都为0.源点到所有的点连边,代表每个节点最后的节点到汇点连边。最后求最小费用最大流。费用为非负时结束查找可行流。得到的费用的绝对值就是不需要删除的操作。

    #include <map>
    #include <string>
    #include <stdio.h>
    #include <vector>
    #include <set>
    #include <algorithm>
    #include <string.h>
    #include <iostream>
    #include <stack>
    #include <queue>
    using namespace std;
    
    const int N=5000;
    
    const int INF=10000000;
    
    struct node
    {
        int u,v,flow,cost,next;
    };
    
    
    node edges[N*100];
    int head[N],e;
    
    
    void add(int u,int v,int flow,int cost)
    {
        edges[e].u=u;
        edges[e].v=v;
        edges[e].cost=cost;
        edges[e].flow=flow;
        edges[e].next=head[u];
        head[u]=e++;
    }
    
    
    void Add(int u,int v,int flow,int cost)
    {
        add(u,v,flow,cost);
        add(v,u,0,-cost);
    }
    
    
    int C[N],F[N],pre[N];
    int visit[N];
    
    
    int SPFA(int s,int t)
    {
        memset(pre,-1,sizeof(pre));
        queue<int> Q;
        Q.push(s);
        int i;
        for(i=0;i<=t;i++) C[i]=INF,F[i]=0,visit[i]=0;
        int u,v,c,f;
        C[s]=0; F[s]=INF;
        while(!Q.empty())
        {
            u=Q.front();
            Q.pop();
    
    
            visit[u]=0;
            for(i=head[u];i!=-1;i=edges[i].next)
            {
                v=edges[i].v;
                c=edges[i].cost;
                f=edges[i].flow;
                if(f>0&&C[v]>C[u]+c)
                {
                    C[v]=C[u]+c;
                    F[v]=min(F[u],f);
                    pre[v]=i;
                    if(!visit[v])
                    {
                        Q.push(v);
                        visit[v]=1;
                    }
                }
            }
        }
        if(C[t]>=0) return 0;
        return F[t];
    }
    
    
    int MCMF(int s,int t)
    {
        int ans=0,i,temp,x;
        while(temp=SPFA(s,t))
        {
            for(i=t;i!=s;i=edges[pre[i]].u)
            {
                x=pre[i];
                ans+=temp*edges[x].cost;
                edges[x].flow-=temp;
                edges[x^1].flow+=temp;
            }
        }
        return ans;
    }
    
    
    int get(int x,int y)
    {
        return x*60+y;
    }
    
    
    int p[5000];
    
    class AncientGameRecord {
    public:
        int minimalRemove(int n, int m, vector <int> x, vector <int> y, string d)
        {
            memset(head,-1,sizeof(head));
            const int K=(int)x.size();
            int cnt=0;
            for(int i=0;i<K;++i)
            {
                int x1=x[i],x2=x[i];
                int y1=y[i],y2=y[i];
                if(d[i]=='D') ++x2;
                if(d[i]=='U') --x2;
                if(d[i]=='L') --y2;
                if(d[i]=='R') ++y2;
    
                if(x2==-1||x2==n||y2==-1||y2==m) continue;
                int c1=get(x1,y1);
                int c2=get(x2,y2);
                Add(p[c1],cnt+1,1,0);
                Add(p[c2],cnt+2,1,0);
                Add(cnt+1,cnt+2,1,-1);
                p[c1]=cnt+1;
                p[c2]=cnt+2;
                cnt+=2;
            }
            int sink=cnt+1;
            for(int i=0;i<n;++i) for(int j=0;j<m;++j) if(p[get(i,j)])
            {
                Add(p[get(i,j)],sink,1,0);
            }
            return K+MCMF(0,sink);
        }
    };
    

      

  • 相关阅读:
    LOJ-10096(强连通+bfs)
    LOJ-10095(缩点的特殊使用)
    LOJ-10094(强连通分量)
    LOJ-10092(最大半连通子图)
    【BZOJ3489】A simple rmq problem(KD-Tree)
    UVA10384 推门游戏 The Wall Pushers(IDA*)
    [SCOI2005]骑士精神(IDA*)
    浅谈A*算法
    【模板】K-D Tree
    【XSY1953】【BZOJ4012】【HNOI2015】开店(动态点分治)
  • 原文地址:https://www.cnblogs.com/jianglangcaijin/p/6378675.html
Copyright © 2020-2023  润新知