[BZOJ1077][SCOI2008]天平(差分约束)

题目描述

传送门

题目大意:有n个砝码,重量只可能为1g,2g或3g,给出一些砝码之间的重量关系。现在将a和b两个砝码放在天平的左边,现在要选出另外两个砝码放在方程的右边,问有多少种方法使得天平左边重、一样重、右边重。(只有结果保证惟一的选法才统计在内)

题解

对于两个砝码x,y,若只考虑+和=的情况,那么可以列出不等式
d(x)-d(y)>=1;d(x)>=d(y),d(y)>=d(x)
对于最小值设一个源点d(0),满足d(0)=3,且d(i)<=d(0);对于最大值设d(0),满足d(0)=1,且d(i)>=d(0)
这样搞成差分约束最短路的模型,跑单源最短路和单源最长路求出每一个点可能的最大值和最小值
然后对于任意的两个点xy,在图中暴力找出xy的关系,即x>y,y>x,x=y或xy不相关
暴力枚举放在右边的两个砝码,再暴力枚举这4个砝码的重量[Min,Max],然后暴力判断这4个砝码的重量关系是否满足在图中的关系,同时满足一种情况才合法
刚开始的时候让我困惑的一点是在暴力枚举重量之后,为什么只需要判断这4个砝码的关系就行了,不需要考虑其它的砝码。实际上这个问题非常蠢,题目保证了至少有一种可行解,在[Min,Max]区间内枚举就保证了其它的点都有可行解

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 55
#define E 10005

int n,a,b,inf,c1,c2,c3;
char s[N];bool flag[N],can[N][N];
int tot,point[N],nxt[E],v[E],c[E],_tot,_point[N],_nxt[E],_v[E],_c[E];
int dis[N],_dis[N],d[N][N];bool vis[N];
queue <int> q;

void add(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
void _add(int x,int y,int z)
{
    ++_tot; _nxt[_tot]=_point[x]; _point[x]=_tot; _v[_tot]=y; _c[_tot]=z;
}
void spfa()
{
    memset(dis,127,sizeof(dis));
    dis[0]=3;vis[0]=1;q.push(0);
    while (!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for (int i=point[now];i;i=nxt[i])
            if (dis[v[i]]>dis[now]+c[i])
            {
                dis[v[i]]=dis[now]+c[i];
                if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
            }
    }
}
void _spfa()
{
    memset(_dis,128,sizeof(_dis));
    _dis[0]=1;vis[0]=1;q.push(0);
    while (!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for (int i=_point[now];i;i=_nxt[i])
            if (_dis[_v[i]]<_dis[now]+_c[i])
            {
                _dis[_v[i]]=_dis[now]+_c[i];
                if (!vis[_v[i]]) vis[_v[i]]=1,q.push(_v[i]);
            }
    }
}
void dfs(int x,int rt)
{
    vis[x]=1;can[rt][x]=1;
    for (int i=point[x];i;i=nxt[i])
        if (!vis[v[i]])
            dfs(v[i],rt);
}
bool check(int A,int B,int I,int J,int a,int b,int i,int j)
{
    if (d[A][B]!=inf)
    {
        if (a<b&&d[A][B]>=0) return 0;
        if (a==b&&d[A][B]) return 0;
        if (a>b&&d[A][B]<=0) return 0;
    }
    if (d[A][I]!=inf)
    {
        if (a<i&&d[A][I]>=0) return 0;
        if (a==i&&d[A][I]) return 0;
        if (a>i&&d[A][I]<=0) return 0;
    }
    if (d[A][J]!=inf)
    {
        if (a<j&&d[A][J]>=0) return 0;
        if (a==j&&d[A][J]) return 0;
        if (a>j&&d[A][J]<=0) return 0;
    }
    if (d[B][I]!=inf)
    {
        if (b<i&&d[B][I]>=0) return 0;
        if (b==i&&d[B][I]) return 0;
        if (b>i&&d[B][I]<=0) return 0;
    }
    if (d[B][J]!=inf)
    {
        if (b<j&&d[B][J]>=0) return 0;
        if (b==j&&d[B][J]) return 0;
        if (b>j&&d[B][J]<=0) return 0;
    }
    if (d[I][J]!=inf)
    {
        if (i<j&&d[I][J]>=0) return 0;
        if (i==j&&d[I][J]) return 0;
        if (i>j&&d[I][J]<=0) return 0;
    }
    return 1;
}
int compare(int A,int B,int I,int J)
{
    int c1=0,c2=0,c3=0;
    for (int a=_dis[A];a<=dis[A];++a)
        for (int b=_dis[B];b<=dis[B];++b)
            for (int i=_dis[I];i<=dis[I];++i)
                for (int j=_dis[J];j<=dis[J];++j)
                    if (check(A,B,I,J,a,b,i,j))
                    {
                        if (a+b>i+j) ++c1;
                        if (a+b==i+j) ++c2;
                        if (a+b<i+j) ++c3;
                    }
    if (c1&&!c2&&!c3) return 1;
    if (!c1&&c2&&!c3) return 2;
    if (!c1&&!c2&&c3) return 3;
    return 0;
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for (int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for (int j=1;j<=n;++j)
            if (s[j]=='+')
                add(i,j,-1),_add(j,i,1);
            else if (s[j]=='='&&i<=j)   
                add(i,j,0),add(j,i,0),_add(i,j,0),_add(j,i,0);
    }
    for (int i=1;i<=n;++i) add(0,i,0),_add(0,i,0);
    spfa();_spfa();
    for (int i=1;i<=n;++i)
    {
        memset(vis,0,sizeof(vis));
        dfs(i,i);
    }
    memset(d,127,sizeof(d));inf=d[0][0];
    for (int i=1;i<=n;++i)
        for (int j=i;j<=n;++j)
        {
            if (can[i][j]&&can[j][i]) d[i][j]=d[j][i]=0;
            else if (can[i][j]) d[i][j]=1,d[j][i]=-1;
            else if (can[j][i]) d[i][j]=-1,d[j][i]=1;
        }
    for (int i=1;i<=n;++i)
        if (i!=a&&i!=b)
            for (int j=i+1;j<=n;++j)
                if (j!=a&&j!=b)
                {
                    int opt=compare(a,b,i,j);
                    if (opt==1) ++c1;
                    if (opt==2) ++c2;
                    if (opt==3) ++c3;
                }
    printf("%d %d %d\n",c1,c2,c3);
}
已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页