题意:有长L的要操作01串nw个,目标01串nw个,长L的操作符串nop个,第i个操作符串每次使用将花费ci。求每个01串变成对应的目标串所需要的最小花费,若无解则输出NP。
思路:把每个01串当作一个点,每次通过操作i变成曾经未生成的状态,就相当于这两个状态之间连了一条权值为ci的边。为求最优解,可以通过记忆化搜索来扩展状态,dj来维护解的数组。
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
#define NUM (1<<20)
#define INF 0x3f3f3f3f
struct node
{
int num,cost;
bool operator>(const node& x)const
{
return cost>x.cost;
}
};
int L,nop,nw;
int c[32];
int mul2[21];
string op[32];
string s1,s2;
bool pd[NUM];
int dp[NUM];
queue <int> ans;
int getnum(string s)
{
int num=0;
for(int i=L-1;i>=0;--i)
if(s[i]=='1')
num+=(1<<(L-1-i));
return num;
}
void solve(int st,int en)
{
memset(pd,false,sizeof(pd));
memset(dp,INF,sizeof(dp));
priority_queue< node , vector<node> , greater<node> > q;
int num1,pos;
node x,y;
x.num=st;
x.cost=0;
dp[st]=0;
q.push(x);
while(!q.empty())
{
x=q.top();
q.pop();
if(x.num==en)
{
ans.push(x.cost);
return ;
}
if(pd[x.num])continue ;
pd[x.num]=true;
for(int i=0;i<nop;++i)
{
num1=0;
for(int j=0;j<L;++j)
{
pos=L-1-j;
switch(op[i][j])
{
case 'N': num1+=(x.num&mul2[pos]);break;
case 'S': num1+=mul2[pos];break;
case 'C': break;
case 'F': num1+=((x.num+mul2[pos])&mul2[pos]);break;
default : break;
}
}
if(dp[num1]>dp[x.num]+c[i]&&!pd[num1])
{
y.num=num1;
y.cost=dp[num1]=dp[x.num]+c[i];
q.push(y);
}
}
}
ans.push(-1);
}
int main()
{
int N;
scanf("%d",&N);
int num1,num2;
mul2[0]=1;
for(int i=1;i<=20;++i)
mul2[i]=mul2[i-1]<<1;
while(N--)
{
scanf("%d%d%d",&L,&nop,&nw);
for(int i=0;i<nop;++i)
{
cin>>op[i];
scanf("%d",&c[i]);
}
for(int i=0;i<nw;++i)
{
cin>>s1>>s2;
num1=getnum(s1);num2=getnum(s2);
solve(num1,num2);
}
for(int i=0;i<nw;++i)
{
num1=ans.front();
ans.pop();
if(num1==-1)
printf("NP ");
else
printf("%d ",num1);
}
printf("\n");
}
}