给定一个有向图,求最少再额外加多少条有向边可以让每个点都能回到自己?可以有自环
数据范围:点数不大于20
贪心水了次..每次添加收益最高的边,边的收益定义为添加完这条边之后增加的能够让点回到自己的点的个数,果然WA了...但是不知道反例在哪里...
另外,即使图时联通的,直接用入度为0的点的个数和出读为0的点的个数取最大值也是不对的
反例如下:1->2,2->3,3->2,4->5,5->4,5->6,4->3
所以使用IDA*搜索
每次枚举需要添加的边的个数,若不能成功再加一
搜索时使用DFS,添加一个剪枝和一个A*的顺序优化
因为要添加的边数一定大于等于入度为0的点的个数和出度为0的点的个数的最大值,所以如果入读为0的点的个数和出度为0的点的个数的最大值比还可以添加的边数多的话,就一定无解,直接return false
然后搜索时按照添加边的收益枚举,先尝试添加收益更高的边,一条边的收益与上边贪心算法时使用的边的收益一样
注意每条边添加完毕之后要维护以下,保证整个关系矩阵还是一个闭包
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int a[20][20];
int n;
struct QueNode {
int i,j,v;
QueNode() {}
QueNode(int ii,int jj,int vv) {
i=ii;j=jj;v=vv;
}
friend bool operator < (const QueNode &a,const QueNode &b) {
return a.v<b.v;
}
};
void print() {
printf("---Matrix---\n");
for (int i=0;i<n;i++) {
for (int j=0;j<n;j++) printf("%d ",a[i][j]);
printf("\n");
}
printf("\n");
}
bool neednext() {
for (int i=0;i<n;i++)
if (a[i][i]==0) return true;
return false;
}
int cal(int x,int y) {
if (x==y) return 1;
int ans=0;
for (int i=0;i<n;i++) {
if (a[i][i]==0&&a[i][x]==1&&a[y][i]==1) ans++;
}
if (a[y][x]==1) {
if (a[y][y]==0) ans++;
if (a[x][x]==0) ans++;
}
return ans;
}
void add(int x,int y) {
for (int i=0;i<n;i++) if (a[y][i]) a[x][i]=1;
a[x][y]=1;
for (int i=0;i<n;i++)
if (a[i][x])
for (int j=0;j<n;j++)
if (a[x][j]) a[i][j]=1;
}
int h() {
int in=0,out=0;
for (int i=0;i<n;i++) {
int tmp=0;
for (int j=0;j<n;j++)
if (a[j][i]) tmp++;
if (tmp==0) in++;
}
for (int i=0;i<n;i++) {
int tmp=0;
for (int j=0;j<n;j++)
if (a[i][j]) tmp++;
if (tmp==0) out++;
}
return in>out?in:out;
}
void cpy(int to[20][20],int from[20][20]) {
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
to[i][j]=from[i][j];
}
bool dfs(int k) {
if (k==0) {
if (neednext()) return false;
else return true;
}
if (h()>k) return false;
int tmp[20][20];
cpy(tmp,a);
priority_queue<QueNode> c;
for (int i=0;i<n;i++) {
for (int j=0;j<n;j++) {
if (a[i][j]==0) c.push(QueNode(i,j,cal(i,j)));
}
}
while (!c.empty()) {
add(c.top().i,c.top().j);
c.pop();
if (dfs(k-1)) return true;
cpy(a,tmp);
}
return false;
}
int main() {
int ans,i,j,k,cas=1;
while (scanf("%d",&n),n>=0) {
for (i=0;i<n;i++)
for (j=0;j<n;j++)
scanf("%d",&a[i][j]);
for (k=0;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if (a[i][k]&&a[k][j]) a[i][j]=1;
//print();
ans=0;
while (!dfs(ans)) {
ans++;
}
if (cas!=1) printf("\n");
printf("Test %d:\n%d\n",cas++,ans);
}
return 0;
}