P2622 关灯问题II

本蒟蒻在开开心心地A了这道题后打开了题解……

?!状压?!

所以此篇题解在这里提供的是一种奇怪的做法。

首先,易得:

每一个灯最后的状态只与最后一次(对这个灯的)操作(1or-1)有关。

所以,正确的最后一次操作中不可以含有-1这个数,因为有-1就意味着最后有灯是打开的就无法达到最后的目标——灯全部关

而对于剩下的每一次操作,我们可以先加一个bool数组used来看每一个位置是否已经拥有了最后一个1(即成功关闭),然后再在没有最后一个1的灯中判断当前操作是否有-1,以此来判断该步操作的可行性。

如果对于任意两种操作,在其都可行的情况下,我们肯定优先选择有效的1比较多的操作。因为对于任意一个合法操作,如果其在当前状态下合法,那么在之后的状态中也必定合法,所以选择1较多的有助于我们降低复杂度,并且尽快的关闭所有的灯。有效的1即:对没有最后一个1的位置进行的1操作。

而最后判断是否可以成功,则可以在每一步操作的选择过程中,如果当前所有操作都有-1或没有1,则无法成功。

如果还有不理解,可以结合代码食用:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
struct HH
{
	int a[15];
	int num;
	bool check;
}s[105];
bool used[15];
int ans=0;
bool cmp(HH a,HH b)
{
	if(a.check==false)
	return false;
	if(b.check==false)
	return true;
	return a.num>b.num;
}//将操作进行排序,寻找在当前情况下合法操作中1的个数最多的
//所以可以直接将不合法操作放至最后 
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		s[i].check=true;
		s[i].num=0;
		for(int j=1;j<=n;++j)
		{
			scanf("%d",&s[i].a[j]);
			if(s[i].a[j]==1)
			{
				++s[i].num;
			}//计算1的个数 
			if(s[i].a[j]==-1)
			{
				s[i].check=false;
			}//判断可行性 
		}
	}
	for(int cnt=n;cnt>0;)
	{
		++ans;
		sort(s+ans,s+1+m,cmp);
		if(s[ans].check==false)
		{
			printf("-1\n");
			return 0;
		}
		if(s[ans].num<=0)
		{
			printf("-1\n");
			return 0;
		}
		for(int i=1;i<=n;++i)
		{
			if(s[ans].a[i]==1&&used[i]==false)
			{
				used[i]=true;
				--cnt;
			}//记录拥有最后一个1的灯
		}
		for(int i=ans+1;i<=m;++i)
		{
			s[i].check=true;
			s[i].num=0;
			for(int j=1;j<=n;++j)
			{
				if(used[j]==true)
				continue;
				if(s[i].a[j]==1)
				{
					++s[i].num;
				}//重新计算1 
				if(s[i].a[j]==-1)
				{
					s[i].check=false;
				}//重新进行可行性判断 
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-07-14 13:45  Point_King  阅读(139)  评论(0编辑  收藏  举报