题意
孩子们有N(1<=N<=20)个音标要去学,按照一般的教学法则,每次学一个音标的时候,要先把比这个音标更基础的音标学会。但由于这个教学法则会出现循环需要的情况导致孩子们无法学完,请你帮助他们用最少的次数改变这个教学法则。
给定一个vectorspellchart,spellchart[i][j]表示如果为’Y’则音标i必须在j前学习完,否则为’N’。你可以将’Y’改成’N’或者’N’改成’Y’,但每个位置只能改变一次。输出最少次数。
思路
<1>很容易想到将每个i,j用图链接形成一个有向图
<2>我们的目标是通过增减边将其变成一个有向无环图(且在对于边的操作中很显然不需要添加边)
<3>如果需要连接两个点(使这两个点合法)需要满足:A-B中没有相互联通的两条边(可以没有边)》
<4>由于只有20个点可以使用状态压缩dp[has]表示形成该点集的最小cost
<5>转移:使点集has中加上一个点i得到新的点集nx是需要将i通向点集has所有的边都删除
得到dp[nw]=min(dp[nw]+dp[nx]+need_change(nx,i));
<6>nx&to[i]数中1的个数即为i可以 到nx中点的个数(利用二进制来表示to[i])
int need_change(int x){
int cnt=0;
while(x){if(x&1)cnt++;x>>=1;}
//while(x){x-=x&(-x);cnt++;}
//while(x){x=x&(x-1);cnt++;}
return cnt;
}
int dfs(int has){
if(has==(1<<n)-1)return 0;
int &ans=dp[has];
if(~ans)return ans;
ans=INF;
for(int i=0;i<n;i++)
if(!(has&(1<<i))){//没有该点
int nx=has+(1<<i);
ans=min(ans,need_change(nx&to[i])+dfs(nx));
//need_chage(nx&to[i])
//表示再加上i点后通过i点的路径可以回到nx中点的个数(即需要删掉边的个数 )
}return ans;
}
n=A.size();
memset(dp,-1,sizeof(dp));
for(int i=0;i<n;i++){
to[i]=0;//利用二进制储存i可以到的点
for(int j=0;j<n;j++)
if(A[i][j]=='Y')to[i]+=1<<j;
}return dfs(0);
}
};