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;
}