PAT危险品装箱—基于邻接图的C语言实现

 

集装箱运输货物时,我们必须特别小心,不能把不相容的货物装在一只箱子里。比如氧化剂绝对不能跟易燃液体同箱,否则很容易造成爆炸。

本题给定一张不相容物品的清单,需要你检查每一张集装箱货品清单,判断它们是否能装在同一只箱子里。

输入格式:

输入第一行给出两个正整数:N (≤10​4​​) 是成对的不相容物品的对数;M (≤100) 是集装箱货品清单的单数。

随后数据分两大块给出。第一块有 N 行,每行给出一对不相容的物品。第二块有 M 行,每行给出一箱货物的清单,格式如下:

K G[1] G[2] ... G[K]

其中 K (≤1000) 是物品件数,G[i] 是物品的编号。简单起见,每件物品用一个 5 位数的编号代表。两个数字之间用空格分隔。

输出格式:

对每箱货物清单,判断是否可以安全运输。如果没有不相容物品,则在一行中输出 Yes,否则输出 No

输入样例:

6 3
20001 20002
20003 20004
20005 20006
20003 20001
20005 20004
20004 20006
4 00001 20004 00002 20003
5 98823 20002 20003 20006 10010
3 12345 67890 23333

样例:

No
Yes
Yes
 

思路分析:

用二维数组——即图去判断两对物品是否是危险品对,可能很多人尝试过内存超出了限制,
其实如果只是判断2物品是否互为危险品,只需要一个二进制位(0/1)就可以判断,因此
可以使用占内存容量最小的char数组来存放图中的每2个物品的信息,若互为危险品则对应
位置为1,否则为0。由于一个char占1个字节,1个字节由8个二进制位,所有通过除以8就
能得到一共需要多少个char用于存放列信息,之后的操作均基于位运算。

 

实现代码:

#include<stdio.h>
#include<stdlib.h>
#define N 10000
int sum1,sum2,size,*NumNumber;
unsigned char** tu;
const char* jg[100];
const char* y="Yes";
const char* n="No";
int compare(const void *,const void *);
int search(int [],int,int,int);
int main(){
    int i,j,k,temp1,temp2,ls1[N],ls2[N],no[2*N],quchong[2*N],nocount=0,qccount=0;
    int inx1,inx2;
    scanf("%d %d",&sum1,&sum2);
    for(i=0;i<sum1;i++){//获取危险物品对的编号
        scanf("%d%d",&temp1,&temp2);
        ls1[i]=temp1;//存放危险物品左边输入的编号
        no[nocount++]=temp1;//存放以上危险对的编号
        ls2[i]=temp2;//存放危险物品右边输入的编号
        no[nocount++]=temp2;//存放以上危险对的编号
    }
    qsort(no,nocount,sizeof(int),compare);//对所有输入危险对编号进行排序
    inx1=-1;
    for(i=0;i<nocount;i++){//去除重复的输入,提取出危险物品对的唯一编号
        if(inx1!=no[i]){
            quchong[qccount++]=no[i];
            inx1=no[i];
        }
    }
    size=(qccount%8==0)?(qccount/8):(qccount/8+1);//计算需要多少个字节才能容纳图的所有二进制位
    tu=(unsigned char**)malloc(sizeof(unsigned char*)*qccount);//分配行指针
    for(i=0;i<qccount;i++){
        *(tu+i)=(unsigned char*)malloc(sizeof(unsigned char)*size);//分配列指针
    }
    NumNumber=(int*)malloc(sizeof(int)*qccount);//呈现升序排序的输入对编号,用于使用二分法搜素
    for(i=0;i<qccount;i++){
        *(NumNumber+i)=quchong[i];
    }
    for(i=0;i<qccount;i++){//初始化图
        for(j=0;j<size;j++){
            *(*(tu+i)+j)=0;
        }
    }
    for(i=0;i<sum1;i++){//使用位运算进行图中位置的赋值
        inx1=search(NumNumber,0,qccount,ls1[i]);
        inx2=search(NumNumber,0,qccount,ls2[i]);
        *(*(tu+inx1)+inx2/8)=*(*(tu+inx1)+inx2/8) | (0x80>>(inx2%8));
        *(*(tu+inx2)+inx1/8)=*(*(tu+inx2)+inx1/8) | (0x80>>(inx1%8));
    }
    int ns[1000];
    _Bool ysno;
    for(i=0;i<sum2;i++){//遍历输入对
        scanf("%d",&temp1);
        for(j=0;j<temp1;j++){
            scanf("%d",&ns[j]);
        }
        ysno=1;
        for(j=0;j<temp1-1 && ysno;j++){
            inx1=search(NumNumber,0,qccount-1,ns[j]);
            if(inx1==-1){
                continue;
            }
            for(k=j+1;k<temp1;k++){
                inx2=search(NumNumber,0,qccount-1,ns[k]);
                if(inx2!=-1){
                    if((*(*(tu+inx1)+inx2/8) & (0x80>>(inx2%8))) || (*(*(tu+inx2)+inx1/8) & (0x80>>(inx1%8)))){//跟图中的位置比较,判断两节点是否连接
                        ysno=0;
                        break;
                    }
                }
            }
        }
        if(ysno){
            jg[i]=y;
        }
        else{
            jg[i]=n;
        }
    }
    for(i=0;i<sum2;i++){
        printf("%s\n",jg[i]);
    }
}
int compare(const void *p1,const void *p2){
    const int *a=(const void *)p1;
    const int *b=(const void *)p2;
    if(*a>*b){
        return 1;
    }
    else if(*a<*b){
        return -1;
    }
    else{
        return 0;
    }
}
int search(int num[],int l,int r,int v){
    int m;
    while(r>=l){
        m=(r+l)/2;
        if(num[m]==v){
            return m;
        }
        if(num[m]>v){
            r=m-1;
        }
        else{
            l=m+1;
        }
    }
    return -1;
}

测试点3、4的内存大概占了12MB,测试点4的运行时间经过多次测试最大200ms左右,最小80ms左右。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qi_____________

希望各位大佬能够施舍几个小破币

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值