【[Offer收割]编程练习赛15 C】过河问题
【题目链接】:http://hihocoder.com/problemset/problem/1516
【题意】
【题解】
状态压缩DP+bfs
这个过河问题能用bfs来搞.涨知识了;
首先;
16个人
容易想到用
即这一边的岸,船上,以及对岸;
对于岸上;
只需要这状态里面,能攻击人的人存在,且它攻击的对象也存在,且能管住这个攻击的人的人不在,则这个状态就是不符合要求的;
而对于船上;
则需要一个掌舵人,否则不行;
然后对于岸上的情况船上的情况也要满足;
还有就是船上的人数的限制了;
设f[i][0..1]分别表示某一边状态为i,然后船在某一边(用0和1表示)的最少渡河次数;
f[(1< < n)-1][0]=0;
然后每次枚举到对岸的人是哪些就好;
枚举的方法很巧妙;
for (int i = (1< < n)-1;i;i=(i-1)&now)
这个&运算保证了,只有当前存在的人才能过去;然后又能把所有情况都涉及到;
最后输出f[(1< < n)-1][1]
程序中can[i]判断i这状态能不能在船上
can2[i]判断i这个状态能不能在岸上
【Number Of WA】
0
【完整代码】
#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ms(x,y) memset(x,y,sizeof x)
typedef pair<int,int> pii;
typedef pair<LL,LL> pll;
const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int N = 18;
const int MAX = 7e4;
int n,m,a,b,c,all;
int son[N],fat[N],boa;
bool can[MAX],can2[MAX];
int f[MAX][2];
queue <pii> dl;
bool check(int statu)
{
if ((statu&boa)==0) return false;
int cnt = 0;
rep1(i,0,n-1)
{
if ((statu>>i)&1)
{
cnt++;
if (((fat[i]&statu)==0) && (son[i]&statu))
return false;
}
}
if (cnt>m) return false;
return true;
}
bool check2(int statu)
{
rep1(i,0,n-1)
{
if ((statu>>i)&1)
{
if (((fat[i]&statu)==0) && (son[i]&statu))
return false;
}
}
return true;
}
int main()
{
//freopen("F:\\rush.txt","r",stdin);
ios::sync_with_stdio(false),cin.tie(0);//scanf,puts,printf not use
cin >> n >> m;
cin >> a >> b >> c;
int x,y;
rep1(i,1,a)
{
cin >> x >> y;
son[x] |= (1<<y);
}
rep1(i,1,b)
{
cin >> x >> y;
fat[y]|= (1<<x);
}
rep1(i,1,c)
{
cin >> x;
boa |= (1<<x);
}
all = (1<<n)-1;
rep2(i,all,0)
{
can[i] = check(i);
can2[i] = check2(i);
}
if (!can2[all]) return cout <<-1<<endl,0;
ms(f,255);
f[all][0] = 0;
dl.push(mp(all,0));
while (!dl.empty())
{
int s = dl.front().fi,k = dl.front().se;dl.pop();
for (int left = s;left;left=(left-1)&s)
{
if (can[left] && can2[all^s^left] && can2[s^left] && can2[all^s])
{
int status = all^s^left;
if (f[status][k^1]==-1)
{
f[status][k^1] = f[s][k]+1;
dl.push(mp(status,k^1));
}
}
}
}
cout << f[all][1] << endl;
return 0;
}