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\n",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\n");
        else
            printf("Unreliable\n");
    }
    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]也应该为枚举的那个值。单调性,二分。

暂无代码;

posted @ 2019-02-15 19:52  Tyouchie  阅读(306)  评论(0编辑  收藏  举报