hdu 2828 Lamp 贪心 + 二分匹配

网上都是DLX搜索, 反正我是没学过DLX搜索, 直接二分匹配,代码比DLX搜索更短。


num[i] 表示i号开关控制的灯的数量

先用 数组Stol[i][j] 表示i号开关控制的第j(根据题目描述,j<=2)个灯。 Stol[i][j].v 是控制的灯的编号, Stol[i][j].sta 是此开关什么状态能打开对应的灯。

首先进行贪心:

1.如果i开关只控制一盏灯,那么此开关的状态必为 Stol[i][0].sta  用viss 标记开关,visl标记灯,表示已经确定

2.如果i开关控制两盏灯,并且Stol[i][0].sta == Stol[i][1].sta, 那么此开关状态必为Stol[i][0].sta, 用viss标记开关,visl标记灯,表示已经确定。


剩下的没用用viss标记的开关中,只有两种情况

1.控制0盏灯

2.控制两盏灯并且 Stol[i][0].sta != Stol[i][1].sta


对剩下未标记的灯和未标记的开关,进行二分匹配。

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#include<ctime>
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
#define ls o<<1
#define rs o<<1|1
#define MS(x,y) memset(x,y,sizeof(x))
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
const int INF = 1<<30;
const int M =  100000007 ;
const double eps = 1e-8;
const int MAXN = 200010;

struct STOL
{
    int v,sta;
}Stol[505][2];
int n,m;
int num[505],match[505];//match和vis两个数组用于二分匹配,num[i]表示第i个开关控制灯的数量
bool visl[505],viss[505],ans[505],vis[505];
bool Find(int x)
{
    int v;

    for(int i = 0; i < 2; ++i)
    {
        v = Stol[x][i].v;
        if(visl[v] || vis[v]) continue;
        vis[v] = 1;
        if(!match[v] || Find(match[v]))
        {
            match[v] = x;
            ans[x] = Stol[x][i].sta;
            return 1;
        }
    }
    return 0;
}
int main()
{
    //fre();
    int tem,swch;
    char ope[5];
    while(~scanf("%d%d",&n,&m))
    {
        MS(num,0);MS(visl,0);MS(viss,0); MS(match,0);
        int flag = 0;
        for(int i = 1; i <= n; ++i)//输入
        {
            scanf("%d",&tem);
            while(tem--)
            {
                scanf("%d%s",&swch,ope);
                Stol[swch][num[swch]].v = i;
                if(ope[1] == 'N') Stol[swch][num[swch]++].sta = 1;
                else Stol[swch][num[swch]++].sta = 0;
            }
        }

        for(int i = 1; i <= m; ++i)//贪心
        {
            if(num[i] == 1)
            {
                viss[i] = 1; ans[i] = Stol[i][0].sta;
                if(visl[Stol[i][0].v]) continue;
                flag++; visl[Stol[i][0].v] = 1;
            }
            else if(num[i] == 2)
            {
                if(Stol[i][0].sta == Stol[i][1].sta)
                {
                    viss[i] = 1; ans[i] = Stol[i][0].sta;
                    if(!visl[Stol[i][0].v])
                    {
                        visl[Stol[i][0].v] = 1;
                        flag++;
                    }
                    if(!visl[Stol[i][1].v])
                    {
                        visl[Stol[i][1].v] = 1;
                        flag++;
                    }
                }
            }
        }

        for(int i = 1; i <= m; ++i)//二分匹配
        {
            if(viss[i]) continue;
            MS(vis,0);
            if(num[i] == 2 && Find(i)) flag++;
        }

        if(flag == n)//输出
        {
            if(ans[1]) printf("ON");
            else printf("OFF");

            for(int i = 2; i <= m; ++i)
                if(ans[i]) printf(" ON");
                else printf(" OFF");
            puts("");
        }
        else printf("-1\n");
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值