隐式图的最大特点是:可以枚举全部的状态(作为定点),操作(作为边)可以由一个状态到另一个状态,答案通常求最少步奏之类。
一般需要hash以及spfa,根据题目的意思选用合适的hash
拿vijos1026为例:点击打开链接
病人的病的情况顶多1<<10种,可以将每一种情况都枚举出来
建图:将每种情况设为一个点u,每种药当做边,可以达到另一种状态v,
最好求一次spfa即可。
这里的状态直接用二进制表示即可。
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define exp 1e-8
#define INF 10000000
#define set(a,b) memset(a,b,sizeof(a));
void bug(string st="bug")
{cout<<st<<endl;}
const int maxn=(1<<10)*200;
struct EDGE
{
int v,next;
}edge[maxn];
int head[(1<<10)+100];
int cnt=0;
int d[200][20];
int n,m,tot;
int getidx(int idx,int i)//前一个状态,用哪个药,返回用药后的状态
{
for(int j=1;j<=n;j++)
if(d[i][j]==1)
idx=idx&~(1<<(n-j));
else if(d[i][j]==-1)
idx=idx|(1<<(n-j));
return idx;
}
void addedge(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int idx)//建图
{
for(int i=1;i<=m;i++)
addedge(idx,getidx(idx,i));
}
void spfa(int s,int t)//求最少步奏
{
int dist[(1<<10)+100];
int vis[(1<<10)+100];
memset(vis,0,sizeof(vis));
for(int i=0;i<=1<<10;i++)
dist[i]=INF;
dist[s]=0;
vis[s]=1;
queue<int>que;
que.push(s);
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(dist[v]>dist[u]+1)
{
dist[v]=dist[u]+1;
if(!vis[v])
{
vis[v]=1;
que.push(v);
}
}
}
}
if(dist[t]<INF)
cout<<dist[t]<<endl;
else
cout<<"The patient will be dead.\n";
}
int main()
{
cnt=0;
cin>>n>>m;
tot=1<<n;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
cin>>d[i][j];
for(int i=tot-1;i>0;i--)
dfs(i);
spfa(tot-1,0);
return 0;
}
vijos1029
一样的隐式图。由一种状态转换到另一种状态。
由于这里符合情况的只有8种,所以最好由这8种情况拓展到其他状态的解,至于hash,选择康拓扩展。
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define exp 1e-8
#define INF 1000000000
#define set(a,b) memset(a,b,sizeof(a));
void bug(string st="bug")
{cout<<st<<endl;}
int fac[20];
int ans[400000];
int c[]={1,-1,3,-3};
struct point
{
int num[10],cost;
}cur;
long long kangtuo(int s[],int tot)//1--tot
{
int idx=0,temp;
for(int i=1;i<tot;i++)
{
temp=0;
for(int j=i+1;j<=tot;j++)
if(s[j]<s[i])
temp++;
idx+=temp*fac[tot-i];
}
return idx+1;
}
bool ok(int i,int k)
{
if(k==-1&&i!=1&&i!=4&&i!=7)
return 1;
if(k==1&&i!=3&&i!=6&&i!=9)
return 1;
if(k==3&&i!=7&&i!=8&&i!=9)
return 1;
if(k==-3&&i!=1&&i!=2&&i!=3)
return 1;
return 0;
}
void bfs()
{
bool vis[400000];
queue<point>que;
memset(vis,0,sizeof(vis));
int num[8][10]={0,2,7,6,9,5,1,4,3,8,
0,2,9,4,7,5,3,6,1,8,
0,4,3,8,9,5,1,2,7,6,
0,4,9,2,3,5,7,8,1,6,
0,6,1,8,7,5,3,2,9,4,
0,6,7,2,1,5,9,8,3,4,
0,8,1,6,3,5,7,4,9,2,
0,8,3,4,1,5,9,6,7,2};
for(int j=0;j<8;j++)
{
for(int i=1;i<=9;i++)
cur.num[i]=num[j][i];
cur.cost=0;
int idx=kangtuo(num[j],9);
ans[idx]=0;
vis[idx]=1;
que.push(cur);
}
while(!que.empty())
{
cur=que.front();
que.pop();
for(int i=1;i<=9;i++)
for(int j=0;j<4;j++)
if(ok(i,c[j]))
{
point temp;
for(int k=1;k<=9;k++)
temp.num[k]=cur.num[k];
swap(temp.num[i],temp.num[i+c[j]]);
temp.cost=cur.cost+1;
int idx=kangtuo(temp.num,9);
if(vis[idx]==0)
{
vis[idx]=1;
ans[idx]=min(ans[idx],cur.cost+1);
que.push(temp);
}
}
}
}
void init()
{
fac[0]=fac[1]=1;
int temp=1;
for(int i=2;i<10;i++)
fac[i]=(temp*=i);
memset(ans,INF,sizeof(ans));
for(int i=0;i<400000;i++)
ans[i]=INF;
}
int main()
{
init();
bfs();
int k[10];
while(scanf("%d %d %d %d %d %d %d %d %d",&k[1],&k[2],&k[3],&k[4],&k[5],&k[6],&k[7],&k[8],&k[9])!=EOF)
{
int idx=kangtuo(k,9);
if(ans[idx]==INF)
cout<<-1<<endl;
else cout<<ans[idx]<<endl;
}
return 0;
}