//Floyd + 二分图最大匹配 //将任务抽象为点,如果任务i完成后还能够赶上任务j的开始时间,则在i-->j连上一条边 //题意是要求分配最少的维修工完成维修工作,那么最坏情况就是每个任务分配一个,共M个 //但是有时多个任务能够由一个维修工完成,如果任务i和任务j之间有边相连,意味着i,j可以同时完成 //那么可以减少一个维修工,这样一来,寻找最大的匹配,再用M减去最大匹配数,所得的结果就是最少的维修工数量了 //前面必须用Floyd算法求出任意两点的最短距离,将-1转化为INF再处理即可 #include<iostream> using namespace std; const int MAXN = 25; const int MAXM = 205; const int INF = 1000000000; int G[MAXN][MAXN]; int Q,M,X,Y; bool vis[MAXM]; int yMatch[MAXM]; int Mat[MAXM][MAXM]; void floyd() { for(int k = 1;k <= Q;++k) for(int i = 1;i <= Q;++i) for(int j = 1;j <= Q;++j) { if(G[i][k] + G[k][j] < G[i][j]) G[i][j] = G[i][k] + G[k][j]; } } struct Job { int city,st,T; }job[MAXM]; bool findPath(int x)//寻找增广路 { for(int y = 1;y <= Y;++y)//这里的编号是从开始 { if(!vis[y] && Mat[x][y]) { vis[y] = 1; if(yMatch[y] == -1 || findPath(yMatch[y]))//如果v未被匹配或者v找到新的增广路 { yMatch[y] = x; return true; } } } return false; } int maxMatch()//返回最大匹配数 { int ans = 0; memset(yMatch,-1,sizeof(yMatch));//初始化 for(int x = 1;x <= X;++x)//注意编号的不同 { memset(vis,0,sizeof(vis)); if(findPath(x)) ++ans; } return ans; } int main() { while(scanf("%d%d",&Q,&M) && Q) { memset(Mat,0,sizeof(Mat)); for(int i = 1;i <= Q;++i) for(int j = 1;j <= Q;++j) { scanf("%d",&G[i][j]); if(G[i][j] == -1) G[i][j] = INF; } floyd(); for(int i = 1;i <= M;++i) scanf("%d%d%d",&job[i].city,&job[i].st,&job[i].T); for(int i = 1;i <= M;++i) { int ed = job[i].st + job[i].T; for(int j = 1;j <= M;++j) { if(i == j) continue; int a = job[i].city; int b = job[j].city; if(G[a][b] + ed <= job[j].st) Mat[i][j] = 1; } } X = Y = M; printf("%d/n",X-maxMatch());//此时的最大匹配数等于最小路径覆盖 } return 0; }