通化邀请赛 I-Difference

http://acm.jlu.edu.cn/joj/showproblem.php?pid=2797

这个题目现场赛的时候没过,想得过于简单了。

主要条件如下

(a) |ai| < T for all i and
(b) (vi, vj) in E <=> |ai-aj| >= T,

首先,如过存在边,那么ai和aj必然一正一负。所以第一步就是黑白染色。

之后不妨假设黑色的为正,白色为负,此时条件b中的绝对值号就可以去掉了。对于一堆的大于等于的约束,可以使用差分约束的方法来做。

对于其中的小于T的约束,变成小于等于T-1。如果spfa的时候使用整数的话,此处的T设为一个300+的数字即可。

不擅长图论,代码略长……



#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

const int intmax=0x3f3f3f3f;//NOTES:intmax
#define mem(a, val) memset(a, val, sizeof(a))//memset(

const int N = 310;
const int T = N;
int mat[N][N];
int mark[N];
int col[N];
int cas, n;

void fun(int i, int c){
    if (col[i] != -1) return;
    col[i] = c;
    for (int j=0; j<n; ++j){
        if (mat[i][j]) fun(j, c^1);
    }
}

int dis[N];
int dd[N][N];
int q[N*N*2], inq[N], cnt[N];

bool spfa(int n){
    mem(dis, 0x3f);
    int head = 0, tail = 0;
    mem(cnt, 0);
    mem(inq, 0);
    dis[n] = 0;
    q[tail++] = n;
    inq[n] = 1;
    while(head != tail){
        int u = q[head ++];
        inq[u] = 0;
        for (int i=0; i<=n; ++i){
            if (dd[u][i] == intmax) continue;
            if (dis[u] + dd[u][i] < dis[i]){
                dis[i] = dis[u] + dd[u][i];
                if (!inq[i]){
                    inq[i] = 1;
                    q[tail++] = i;
                    cnt[i] ++;
                    if (cnt[i] > n) return false;
                }
            }
        }
    }
    return true;
}

bool judge(int n){
    mem(col, 0xff);
    mem(mark, 0xff);
    for (int i=0; i<n; ++i) fun(i, 0);
    for (int i=0; i<n; ++i) for (int j=0; j<n; ++j){
        if (mat[i][j] && col[i] == col[j]) return false;
    }
    mem(dd, 0x3f);
    int a, b;
    for (int i=0; i<n; ++i){
        for (int j=0; j<n; ++j){
            if (col[i] == col[j]) continue;
            a = i, b = j;
            if (col[i] == 1) swap(a, b);
            if (mat[a][b]){
                dd[a][b] = -T;
            }else{
                dd[b][a] = T - 1;
            }
        }
    }
    for (int i=0; i<n; ++i){
        if (col[i] == 0){
            dd[i][n] = 0;
            dd[n][i] = T - 1;
        }else {
            dd[n][i] = 0;
            dd[i][n] = T - 1;
        }
    }
    return spfa(n);
}

int main(){
    scanf("%d", &cas);
    while(cas --){
        scanf("%d", &n);
        for (int i=0; i<n; ++i) for (int j=0; j<n; ++j){
            scanf("%1d", &mat[i][j]);
        }
        if (judge(n)){
            puts("Yes");
        }else{
            puts("No");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值