P2622 关灯问题II (状态压缩,最短路)

题目链接


Solution

这道题算是很经典的状压问题了,好题.
考虑到 \(n\) 的范围仅为 \(10\) , 那么也就是说所有状态压起来也只有 \(1024\) 种情况.
然后我们发现 \(m\) 居然小于 \(100\) .
于是可以 \(O(nm)\) 处理出每一种情况可以到达的结果.
然后形成一个有向图,然后直接跑 \(SPFA\) 就好了.

Code

/*	
Problem: P2622
Time : Day -96
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1008;
struct sj{
	int to;
	int next;
}a[maxn*maxn*2];
int head[maxn],size;
int opt[maxn][20],n,m;

int read()
{
    char ch=getchar(); int f=1,w=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){w=w*10+ch-'0';ch=getchar();}
    return f*w;
}

void add(int x,int y)
{
	a[++size].to=y;
	a[size].next=head[x];
	head[x]=size;
}
int c[20]={0},b[20]={0};
void pre()
{
	int tot=(1<<n)-1;
	for(int i=tot;i>0;i--)
	{
		for(int j=n-1;j>=0;j--)
		c[j]=(i>>j)&1;
		for(int k=1;k<=m;k++)
		{
			for(int j=n-1;j>=0;j--)
			{
				if(opt[k][j]==1&&c[j]==1)
				b[j]=0;
				else if(opt[k][j]==-1&&c[j]==0)
				b[j]=1;
				else b[j]=c[j];
			}
			int fuck=0;
			for(int j=n;j>=0;j--)
			fuck=fuck*2+b[j];
			add(i,fuck);
		}
	}
}

queue<int>q;
int dis[maxn],v[maxn];
void spfa()
{
	int s=(1<<n)-1;
	memset(dis,127,sizeof(dis));
	dis[s]=0;
	q.push(s);
	v[s]=1;
	while(q.empty()!=1)
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int tt=a[i].to;
			if(dis[x]+1<dis[tt])
			{
				if(!v[tt])
				q.push(tt),
				v[tt]=1;
				dis[tt]=dis[x]+1;
			}
		}
	}
}

int main()
{
	n=read();
	m=read();
	for(int i=1;i<=m;i++)
	for(int j=0;j<n;j++)
	opt[i][j]=read();
	pre();
	spfa();
	if(dis[0]<123456)
	cout<<dis[0]<<endl;
	else
	cout<<"-1"<<endl;
}
posted @ 2018-08-06 20:59  Kevin_naticl  阅读(358)  评论(0编辑  收藏  举报