【Test 2016-10-3】栈/分治+Trie+spfa最短路

本文介绍了如何利用栈和分治策略解决布尔表达式求值问题,以及在解决求最大异或值问题时,从暴力解法到使用Trie树优化的过程。此外,还讨论了在无向带权图中,通过SPFA算法找到白点到黑点最小代价路径的问题,以及如何处理特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)boolean

  • 【题目描述】:
    输入一个布尔表达式,请你输出它的真假值。
    比如:( V | V ) & F & ( F | V )
    V 表示 true,F 表示 false,& 表示与,| 表示或,! 表示非。
    上式的结果是 F
    注:! 的优先级最高,& 次之,| 最低
  • 【Sample Input】
    !V | V & V & !F & (F | V ) & (!F | F | !V & V)
  • 【Sample Output】
  • 【注】对于 60% 的数据,表达式总长度 ≤ 10^3;对于 100% 的数据,表达式总长度 ≤ 10^6

【题解】看到这题反应就是表达式求值,妥妥20分钟打个栈过了。听说许多god被多个连续!给坑了,我的做法想想不会出事。。。当然表达式求值用栈或者分治都是妥妥过的。


//开两个栈一个记值一个记符号,符号取优先级判断
#include <cstdio>
#include <cstring>
int l,t,top,z[1000005],c[1000005];
char s[1000005];
int main()
{
        for (char ch;~scanf("%c",&ch);)
            if (ch=='(' || ch=='!' || ch=='&' || ch=='|' || ch==')' 
                || ch=='V' || ch=='F') s[++l]=ch;
        s[0]='(';s[l+1]=')';l+=2;
        for (int i=0;i<l;++i)
        {
            if (s[i]=='V' || s[i]=='F') z[++t]=(s[i]=='V');
            else 
            {
                int k=0;
                if (s[i]=='(') k=0;
                if (s[i]=='!') k=1;
                if (s[i]=='&') k=2;
                if (s[i]=='|') k=3;
                if (s[i]==')') k=4;
                for (;top && k>c[top];c[top--]=0)
                {
                    if (!c[top]) break;
                    if (c[top]==1) z[t]=!z[t];
                    if (c[top]==2) z[t-1]&=z[t],--t;
                    if (c[top]==3) z[t-1]|=z[t],--t;
                }
                if (k==4) c[top--]=0;
                else c[++top]=k;
            }
        }
        if (z[1]) printf("V");
        else printf("F");
    return 0;
}

(二)xor

  • 【题目描述】:
    给出 n 个数 a 1 ..a n ,求 a i xor a j , (i不等于j) 的最大值
  • 【Sample Input】(第一行一个数 n 接下一行 n 个数 a 1 ..a n)
    4
    3 6 7 7
  • 【Sample Output】(一行一个数 ans, 表示 a i xor a j , (i不等于j) 的最大值)
    5
  • 【注】对于 30% 的数据,n ≤ 1000;对于另外 20% 的数据,数据保证最后的 ans 一定是 2^k−1;对于 70% 的数据,n ≤ 10^5 ,0 ≤ a i ≤ 10^7 ;对于 100% 的数据,n ≤ 10^5 ,0 ≤ a i ≤ 10^16 。

【题解】
{30%} n^2暴力妥妥的不说了 0 0
{50%} 暴力基础上特判枚举一下 ans=2^k−1 的特殊情况 0 0
{80%} 暴力随意YY,考试的时候脑抽没想到字典树,就随便乱打。贪心的想,较大数和较小数由于位数差多,其乘积为最大的可能性较大。所以我们枚举的时候优先将大的和小的匹配,开个clock看看时间如果不太够了就跳出循环。还可以开个变量记一下可能的最大值,如果达到就break。这样80分。
{100%} 字典树。(%%% qzqzgfy 当初是这位god教的Trie)首先将数都转成二进制,并建出一棵字典树。接着我们将所有的数扫描一遍。贪心知,数位从高往低扫描时,我们尽量在树上取与当前数的当前位不同的,使它亦或值为1,从而使答案尽可能大。扫一遍取最大值即为答案。二进制位数最多为54位,所以复杂度O(54*n);

#include <cstdio>
#include <iostream>
#define LL long long 
LL ans,a[100005];
int n,tot,f[8000000][2];
int main()
{
    scanf("%d\n",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%lld",&a[i]);LL x;
        for (int dep=0,j=55;j>=0;--j,dep=f[dep][x])
            if (!f[dep][x=(a[i]>>j)&1]) f[dep][x]=++tot;
    }
    for (int i=1;i<=n;++i)
    {
        LL t=0,x;
        for (int dep=0,j=55;j>=0;--j)
            if (f[dep][(x=(a[i]>>j)&1)^1]) dep=f[dep][x^1],t|=1ll<<j;
            else dep=f[dep][x];
        ans=std::max(ans,t);
    }
    printf("%lld\n",ans);
    return 0;
}

(三)minimum

  • 【题目描述】:
    给出一幅由 n 个点 m 条边构成的无向带权图。
    其中有些点是黑点,另外点是白点。
    现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个,可以选取其中任意一个) , 我们想要使得花费的代价最小。请问这个最小代价是多少?
    注意:最后选出的边保证每个白点到黑点的距离任然是最短距离。

  • 【Sample Input】(第一行两个整数 n,m;第二行 n 个整数,0 表示白点,1 表示黑点;接下来 m 行,每行三个整数 x,y,z,表示一条连接 x 和 y 点,权值为 z 的边。)
    5 7
    0 1 0 1 0
    1 2 11
    1 3 1
    1 5 17
    2 3 1
    3 5 18
    4 5 3
    2 4 5

  • 【Sample Output】(如果无解,输出 impossible;否则,输出最小代价)
    5
  • 【注】对于 30% 的数据,1 ≤ n ≤ 10,1 ≤ m ≤ 20;对于 100% 的数据,1 ≤ n ≤ 10^5 ,1 ≤ m ≤ 2 × 10^5 ,1 ≤ z ≤ 10^9 。

【题解】三个半小时考试,前两题没好好搞都在搞这题最短路,然后文件名写错了QAQ
(”god救我呀!“)
{30%} 暴力水过;
{100%} 将所有黑点缩成一个点向白点跑最短路{spfa},因为这张图其实很稀疏,时间跑出来也挺快的。缩点是因为显然黑点之间的连边没有意义。接着,将白点扫一遍,如果存在白点到不了黑点,输出impossible;否则设d[i]为i点的最小代价(这个代价不包括其它白点为走到黑点已经选取的边权):
d[v]=min(d[u],e.val) {dis[u]+e.val==dis[v]}
也就是,如果点v能通过点u走到一个距离它最近的黑点,就比较更新最小值。


#include <cstdio>
#include <iostream>
#define inf 1000000000
#define LL long long
#define INF 1ll<<60
struct edge{ int to,nxt,s;}e[2000005];
int i,n,m,x,y,z,cnt,tot,fi[1000005],q[10000005],a[1000005],d[1000005];
LL ans,dis[1000005];bool bo[1000005];
    void add(int u,int v,int w)
    {
        e[++cnt].to=v;e[cnt].s=w;
        e[cnt].nxt=fi[u];fi[u]=cnt;
    }
    void spfa()
    {
        int h=1,t=1;
        for (int i=2;i<=tot;++i) dis[i]=INF;
        for (bo[q[1]=1]=true;h<=t;bo[q[h++]]=false)
            for (int i=fi[q[h]];i;i=e[i].nxt)
                if (dis[q[h]]+e[i].s<dis[e[i].to])
                {
                    dis[e[i].to]=dis[q[h]]+e[i].s;
                    if (!bo[e[i].to]) bo[q[++t]=e[i].to]=true;
                }
        for (int i=2;i<=tot;++i) d[i]=inf;
        for (int i=1;i<=tot;++i)
            for (int j=fi[i];j;j=e[j].nxt)
                if (dis[i]+e[j].s==dis[e[j].to])
                    d[e[j].to]=std::min(d[e[j].to],e[j].s);
    }
int main()
{
    scanf("%d%d\n",&n,&m);
    tot=1;
    for (int i=1;i<=n;++i)
    {
        int x;scanf("%d",&x);
        if (!x) a[i]=++tot;
    }
    for (int i=1;i<=m;++i)
    {
        int u,v,w;scanf("%d%d%d\n",&u,&v,&w);
        if (!a[u]) u=1;else u=a[u];
        if (!a[v]) v=1;else v=a[v];
        if (u==v) continue;
        add(u,v,w);add(v,u,w);
    }
    spfa();
    for (int i=2;i<=tot && ans!=-1;++i)
        if (d[i]!=inf) ans+=d[i];
        else ans=-1;
    if (ans<0) printf("impossible");
    else printf("%lld\n",ans);
    return 0;
}

【题外话】
考完试,有个神奇的学弟不服老师的标解,出了个神奇的数据卡掉了标程,原来标程是老师的学生写的OoO,然后老师拿了两个大牛的程序最终跑过了。
【Input】
5 4
1 0 0 0 1
1 2 3
2 3 2
3 4 1
4 5 4
【Output】
8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值