1. 程式人生 > >狀壓dp USACO 關燈問題Ⅱ

狀壓dp USACO 關燈問題Ⅱ

題意:nn盞燈,mm個按鈕,一開始所有燈都是亮的。按下ii按鈕對於第jj盞燈,是下面33中效果之一:如果a[i][j]a[i][j]11,若此燈是亮的,把它關上,否則不管;如果為1-1的話,若此燈是暗的,那麼把它開啟,否則也不管;如果是00,無論這燈是否開,都不管。求關掉所有燈的最小操作次數。(n<=10)(n<=10)

狀壓dp經典開關燈問題,設f[i]f[i]表示燈處於狀態ii時需要的最小操作次數。因為燈一開始是全開的,所以我們從大到小列舉所有狀態ii,對於每一種狀態,列舉每一個按鈕jj和每一盞燈kk,我們設n

ownow為此時的狀態,若a[j][k]=1a[j][k]=1並且第kk盞燈是亮的,即ii&(1<<(k1))!=0(1<<(k-1))!=0,就改變此時的nownow,即nownow^=(1<<(k1))=(1<<(k-1)),將該盞燈滅掉。當a[j][k]=1a[j][k]=-1時也相同。最後f[now]=min(f[now],f[i]+1)
f[now]=min(f[now],f[i]+1)
,讓此次操作來更新答案。

#include<bits/stdc++.h>
using namespace std;
int n,m,f[100000],a[1000][1000];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;++i)
		for(int j=1;j<=n;++j)
			scanf("%d",&a[i][j]);
	for(int i=0;i<(1<<n);++i)
		f[i]=999999999
; f[(1<<n)-1]=0; for(int i=(1<<n)-1;i>=0;--i) { for(int j=1;j<=m;++j) { int now=i; for(int k=1;k<=n;++k) { if(a[j][k]==0) continue; if(a[j][k]==1&&(i&(1<<(k-1)))) now^=(1<<(k-1)); if(a[j][k]==-1&&!(i&(1<<(k-1)))) now^=(1<<(k-1)); } f[now]=min(f[now],f[i]+1); } } if(f[0]==999999999) { cout<<"-1"; return 0; } cout<<f[0]; return 0; }