• D2欧拉路,拓扑排序,和差分约束


    第一题:太鼓达人;BZOJ3033

    题意:给出k,求一个最长的M位01串,使其从每一个位置向后走k个得到 的M个k位01串互不相同(最后一个和第一个相邻,即是一个环)。输出 字典序最小的答案。 2 ≤ k ≤ 11。

    结论+爆搜;

    第二问我们将每个k二进制数看成一个点,在它后面加上0/1就能得 到两个新数,我们从这个数向两个新数连边,于是这就变成了求一个欧 拉回路的问题。显然此题是存在欧拉回路的第一问答案为2的k次方 ,第二问暴力求欧拉回路即可。

    发现sjh的注释很详细,所以贿赂搞来了一个满满注释的代码,可以更好地理解;

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e3+50;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int n,t,ans[maxn];
    bool used[maxn];
    inline bool eular(int x,int k)//x是当前子串的十进制表示,k是当前查询次
    {
        if (used[x]) return 0;
        used[x]=1,ans[k]=x&1;//统计答案,&的规则是:有零则零,否则为一,返回末位数字
        if (k==t) return 1;//查询结束
        if (eular((x<<1)&(t-1),k+1)) return 1;
        if (eular((x<<1|1)&(t-1),k+1)) return 1;//将每个二进制数看成一个点,将他前面的数删去,并在它后面加上0/1就能得到两个新数
        used[x]=0;//回溯,以免影响之后的dfs
        return 0;
    }
    int main()
    {
        read(n);
        t=1<<n;
        printf("%d ",t);
        eular(t-2,1);//从每位走n位子串,可得到的完整子串数量为2^n-2
        for (int i=1;i<=t;++i)
            printf("%d",ans[i]);
        return 0;
    }
    View Code

    意外发现邱神的代码,但没有写题解,所以只能看一下

    #include<bits/stdc++.h> 
    using namespace std;
    int read()
    {
        int a;
        cin>>a;
        return a;
    }
    int i,k,m;
    struct node
    {
        int x,deep;
    }o[10010];
    bool Orz(node a,node b)
    {
        return a.deep<b.deep;
    }
    void dfs(int now,int deep)
    {
        if(deep==m+1)
        {
            now=now%(m/2);
            now=now*2;
            if(now)
                return ;
            sort(o,o+m,Orz);
            for(i=1;i<k;i++)
                cout<<0;
            for(i=0;i<=m-k;i++)
                cout<<o[i].x%2;
            
            exit(0) ;
        }
        //这里应该有一个关于deep和now的if使得dfs停下来并输出
        now=now%(m/2);
        now=now*2;
        if(!o[now].deep)
        {    
            o[now].deep=deep;
            dfs(now,deep+1);
            o[now].deep=0;
        }
        now++;
        if(!o[now].deep)
        {
            o[now].deep=deep;
            dfs(now,deep+1);
            o[now].deep=0;
        }
        return ;
    }
    int main()
    {    
        //freopen("123.in","r",stdin);
        k=read();m=int(pow(2,k*1.0));
        cout<<m<<' ';
        for(i=0;i<m;i++)
            o[i].x=i;
        o[0].deep=1;
        dfs(0,2);
    }
    View Code

    第二题;poj1386

    我们不必要知道每个单词的每个字母,只需要记录首字母和尾字母,所以转换成了一个判断欧拉回路的题目;

    首先我们需要知道两个字母知否在一个同一个联通块上,我们可以用并差集来写,然后判断欧拉回路,方法不再陈述;

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    #define MAX 26
    using namespace std;
    int out[MAX],in[MAX],flag[MAX],p[MAX],father[MAX];
    int Find(int x)
    {
        if(father[x]!=x)
            father[x]=Find(father[x]);
        return father[x];
    }
    void Union(int a,int b) 
    {
        int p=Find(a);
        int q=Find(b);
        if(p!=q)    father[q]=p;
    }
    int main()
    {
        int i,k,count,a,b,nc,n;
        char str[1002];
        cin>>nc; 
        while(nc--)
        {
            for(i=0;i<26;i++)
                father[i]=i; 
            memset(in,0,sizeof(in));
            memset(out,0,sizeof(out));
            memset(flag,0,sizeof(flag));
            cin>>n;
            for(i=1;i<=n;i++)
            {
                cin>>str;
                a=str[0]-'a';
                b=str[strlen(str)-1]-'a';
                Union(a,b);   
                in[b]++;  
                out[a]++;
                flag[a]=flag[b]=1;
            }
            count=0;
            for(i=0;i<26;i++)
            {
                father[i]=Find(i);
                if(flag[i] && father[i]==i)
                    count++; 
            }
            if(count>1)
            {
                cout<<"The door cannot be opened."<<endl;
                continue;
            }
            k=0;
            for(i=0;i<26;i++)
            {
                if(flag[i] && in[i]!=out[i])
                    p[k++]=i;
            }
            if(k==0)
            {
                cout<<"Ordering is possible."<<endl;
                continue;
            }
            if(k==2 && ((out[p[0]]-in[p[0]]==1 && in[p[1]]-out[p[1]]==1)
            || (out[p[1]]-in[p[1]]==1 && in[p[0]]-out[p[0]]==1)) )      
            {
                cout<<"Ordering is possible."<<endl;
                continue;
            }
            cout<<"The door cannot be opened."<<endl;
        }
        return 0;
    }
    View Code

    第三题:CF516/B

    题目大意:给一个n × m的矩阵,问在其中空白处放置1 × 2, 2 × 1的骨牌的方案 是否存在且唯一。 1 ≤ n, m ≤ 1000;

    我们可以用度来表示这个节点当前周围放置的个数;

    那么对应度为1的点是一定确定了放置这个点的方式的(显然),对应这个点的四周四个位子中,此时一定只有一个点,那么对应将此处和那个位子直接放置这个1 × 2的形状即可。然后处理完这个点之后,对其他点进行操作,如果再发现了度 为1的点,入队。一直处理下去这样的类似拓扑排序的过程即可。 如果最后还剩下了,说明无法完全覆盖,或者方案不止一个;

    所以这个题是拓扑排序的一个拓展;

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <queue>
    
    using namespace std;
    
    typedef struct Point
    {
        int x,y;
    }Point;
    
    Point a[4000000];
    int in[2002][2002];
    char p[2002][2002];
    int dx[] = { 0, 0, 1,-1};
    int dy[] = { 1,-1, 0, 0};
    char ch[] = "><v^";
    char st[] = "<>^v";
    int m,n;
    int k;
    bool check(int x,int y) {return x > 0 && x <= m && y > 0 && y <= n;}
    void ans(int x,int y)
    {
        int i,xx,yy,cnt = 0;
        for(i = 0; i < 4; ++i)
        {
            xx = x+dx[i];
            yy = y+dy[i];
            if(check(xx,yy)&&p[xx][yy]=='.') cnt++;
        }
        in[x][y]=cnt;
    }
    bool topo()
    {
        int i,j,x,y,xx,yy,xxx,yyy,cnt = 0;
        queue <pair<int,int> > q;
        for(j = 0; j <k; ++j)
        {
            x = a[j].x;
            y = a[j].y;
            if(in[x][y] == 1)
            {
                q.push(pair<int,int> (x,y));
                in[x][y] = 0;
            }
        }
        pair <int,int> m;
        while(!q.empty())
        {
            m = q.front();
            q.pop();
            x = m.first;
            y = m.second;
            in[x][y]--;
            for(j = 0; j < 4; ++j)
            {
                xx = x+dx[j];
                yy = y+dy[j];
                if(check(xx,yy) && p[xx][yy] == '.')
                {
                    in[xx][yy] = 0;
                    p[xx][yy] = ch[j];
                    p[x][y] = st[j];
                    for(int f = 0; f < 4; ++f)
                    {
                        xxx = xx+dx[f];
                        yyy = yy+dy[f];
                        if(check(xxx,yyy) && p[xxx][yyy] == '.')
                        {
                            ans(xxx,yyy);
                            if(in[xxx][yyy] == 1) q.push(pair<int,int>(xxx,yyy));
                        }
                    }
                    cnt+=2;
                }
            }
        }
        return cnt == k;
    }
    
    int main()
    {
        int i,j;
        scanf("%d %d",&m,&n);
        k = 0;
        for(i = 1; i <= m; ++i)
        {
            scanf("%s",p[i]+1);
            for(j = 1; j <= n; ++j)
                if(p[i][j] == '.')
                {
                    a[k].x = i;
                    a[k++].y = j;
                    in[i][j] = 0;
                    if(check(i-1,j) && p[i-1][j] == '.')
                    {
                        in[i-1][j]++;
                        in[i][j]++;
                    }
                    if(check(i,j-1) && p[i][j-1] == '.')
                    {
                        in[i][j-1]++;
                        in[i][j]++;
                    }
                }
        }
    
        if(topo())
        {
            for(i = 1; i <= m; ++i) printf("%s
    ",p[i]+1);
        }else puts("Not unique");
    
        return 0;
    }
    View Code

    第四题:poj3169

    题目大意:有n头牛,他们按顺序排成了一排,有些牛关系比较好,他们的距 离不能超过某个距离,还有些牛关系不好,他们之间的距离不能小于某 个距离,可能会有多头牛挤在同一位置上,问1号牛和n号牛之间的最大 距离是多少,如果不存在满足条件的排列则输出−1,如果距离无限大则 输出−2。 2 ≤ n ≤ 1000。

    查分约束版子题,由题意列出不等式根据三角不等式,建图,跑最短路即可;

    题解:令d[i]表示第i头牛的位置,因为牛按顺序排列,则有d[i] ≤ d[i + 1], 即d[i] − d[i + 1] ≤ 0。 关系不好的牛有d[a] + d ≤ d[b]。 关系好的牛有d[a] + D ≤ d[b], 即d[a] − d[n] ≤ −D 。 跑最短路即可。

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    const int MAX=1e6+5;
    const int inf=0x3f3f3f3f;
    using namespace std;
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*f;
    }
    int n,x,y,vis[MAX*2],dis[MAX*2],lin[MAX*2],tot,ans[MAX*2],a,b,c;
    struct gg
    {
        int y,next,v;
    }an[MAX];
    void init(int x,int y,int z)
    {
        an[++tot].y=y;
        an[tot].v=z;
        an[tot].next=lin[x];
        lin[x]=tot;
    }
    queue<int> q;
    int spfa(int s,int n)
    {
        for (int i=0;i<MAX;++i) dis[i]=inf;
        memset(ans,0,sizeof(ans));
        memset(vis,0,sizeof(vis));
        ++ans[s];
        dis[s]=0;
        vis[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            vis[x]=0;
            for (int i=lin[x];i;i=an[i].next)
            {
                int y=an[i].y;
                if (dis[y]>dis[x]+an[i].v)
                {
                    dis[y]=dis[x]+an[i].v;
                    if (!vis[y])
                    {
                        vis[y]=1,q.push(y);
                        ++ans[y];
                        if (ans[y]>n) return -1;
                    }
                }
            }
        }
        if(dis[n]==inf) return -2;
        return dis[n]; 
    }
    int main()
    {
        int n=read(),x=read(),y=read();
        for (int i=1;i<n;++i)
            init(i+1,i,0);
        for (int i=1;i<=x;++i)
        {
            int a=read(),b=read(),d=read();
            init(a,b,d);
        }
        for (int i=1;i<=y;++i)
        {
            int a=read(),b=read(),d=read();
            init(b,a,-d);
        }
        cout<<spfa(1,n)<<endl;
        return 0;
    }
    View Code

    第五题:poj2983

    题目大意:银河系中有n个防御站排成一条直线,给定m条信息,每条信息的 格式如下: P A B X代表A站在B站北方X光年处。 V A B 代表A站在B站北方,而且距离至少为1光年。 问是否存在这样的一个防御战排列满足上述要求,是输出Reliable,否输 出Unreliable。

    dist[A] − dist[B] ≥ X,表示A在B北边至少X光年位置。变形为: dist[B] <= dist[A] − X;所以A指向B有条权值为-X的边。 对于A − B = X,建两条边dist[B] ≤ dist[A] − X, dist[A] ≤ dist[B] + X。

    SPFA存在负权环则说明不符合实际情况。

    #include <iostream>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cstdio>
    #include <set>
    #include <math.h>
    #include <algorithm>
    #include <queue>
    #include <iomanip>
    #include <map>
    #include <cctype>
    #define INF 0x3f3f3f3f
    #define MAXN 1000005
    #define Mod 1000000007
    using namespace std;
    struct Edge
    {
        int v,w,next;
    };
    Edge edge[MAXN<<1];
    int head[MAXN],n,m,e,vis[MAXN],dis[MAXN];
    int p[MAXN];
    void add(int u,int v,int w)
    {
        edge[e].v=v;
        edge[e].w=w;
        edge[e].next=head[u];
        head[u]=e;
        e++;
    }
    bool spfa(int u)
    {
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;++i)
            dis[i]=-INF;
        dis[u]=0;
        queue<int> q;
        q.push(u);
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            vis[u]=0;
            p[u]++;
            if(p[u]>n)
                return false;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].v,w=edge[i].w;
                if(w+dis[u]>dis[v])
                {
                    dis[v]=w+dis[u];
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return true;
    }
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            e=0;
            memset(head,-1,sizeof(head));
            memset(p,0,sizeof(p));
            for(int i=0;i<m;++i)
            {
                int a,b,c;
                char op;
                cin>>op;
                if(op=='P')
                {
                    scanf("%d%d%d",&a,&b,&c);
                    add(a,b,c);
                    add(b,a,-c);
                }
                else
                {
                    scanf("%d%d",&a,&b);
                    add(a,b,1);
                }
            }
            for(int i=1;i<=n;++i)
                add(0,i,0);
            if(spfa(0))
                printf("Reliable
    ");
            else
                printf("Unreliable
    ");
        }
        return 0;
    }
    View Code

    第六题:hdu3440

    题目大意:T组数据,有n栋房子在一条直线上,给出每栋房子的高度和开始时 的相对位置,可以移动一些房子,但不能改变这些房子的相对位置,现 在从最矮的房子开始,每次跳至第一个比它高的房子,经过n − 1跳到达 最高的房子。而且每次跳跃的水平距离最大是D,房子不能在同一位置, 只能在整点位置。问最矮的房子和最高的房子之间的最大距离可以是多 少?如果不能从最矮的房子到达最高的房子则输出−1。 1 ≤ T ≤ 500, 1 ≤ N ≤ 500。

    令d[i]表示第i栋房子与第一栋房子之间的最大距离,那么我们要求的就是的d[n]。 首先每栋房子之间的相对位置已经确定且不能在同一位置,那么d[i + 1]>d[i],即d[i + 1] − d[i] ≥ 1,即d[i] − d[i + 1] ≤ −1。 还有一个条件是相邻两个要跳跃的距离不可以大于D,可以写出另一个方程。有一点小问题就是建边的方向和跳跃的方向不同,我们要保证是向一个方向建图的。 若从左到右建边,如果1在n的左面就从1到n跑SPFA,反之则从n到1跑最短路了。

    暂无代码;

    第七题:poj1275

    题解:每天的工作情况都是一样的,我们只需要求出一天的即可。根据题 意,令s[i]为一天内前i + 1个小时录用的人数。 ◆ 如果i ≥ 7,则s[i] − s[i − 8] ≥ R[i] ◆ 如果0 ≤ i < 7,则可以推出s[23] − s[i + 16] + s[i] ≥ R[i] ◆ 同时每个时刻录用的人数有个上限,假设第i时刻最多可以 录用的人数为b[i],则对于每一 个i有0 ≤ s[i] − s[i − 1] ≤ b[i]。 第二个不等式中包含3个s数组的变量,这该怎么建图呢? 枚举s[23],那么这个量就是已知的了,因此不等式可以变 为s[i] − s[i + 16] ≥ R[i] − s[23]。 既然s[23]是枚举的一天的录用人数的最小数目,我们建图之后求出 的s[23]也应该为枚举的那个值。单调性,二分。

    暂无代码;

  • 相关阅读:
    Ubuntu 18.04 新系统 允许root远程登录设置方法
    《软件需求》读书笔记03
    第十一周总结
    python 遍历迭代器iteration与list的区别
    第九周总结
    大学生运动情况调研计划
    系统需求分析08
    系统需求分析07
    系统需求分析06
    系统需求分析05
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10385615.html
Copyright © 2020-2023  润新知