/*
题意: 一家维修公司,服务的 Q 街区。每个街区有一定距离, 一天公司收到M修理任务,第i个任务发生在第i个街区,并且每个任务都有起始时间 ti,完成一个任务需要di时间.修理工必须完成一个任务后,再去另一个.问要完成这M个任务最少需要多少个修理工?
思路:有向图的最小路径覆盖。先用floyd求出两点间的最短距离,然后建图 若第j个街区任务的开始时间大于等于第i个街区的任务完成时间 + 路上花费时间 则i到j间连一条边 建立二分图 求最大匹配 。ans = 顶点数 - 最大匹配数。
POJ 3216
*/
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
int q,m;
int dict[30][30],g[220][220],linker[220],vis[220];
struct Edge{
int id;
int x,y;
}task[220];
//最大匹配算法模板
int DFS(int u){
for(int i=1;i<=m;i++){
if(g[u][i]&&!vis[i]){
vis[i]=1;
if(linker[i]==-1||DFS(linker[i])){
linker[i]=u;
return 1;
}
}
}
return 0;
}
int Hungary(){
int ans=0;
memset(linker,-1,sizeof(linker));
for(int i=1;i<=m;i++){
memset(vis,0,sizeof(vis));
if(DFS(i))
ans++;
}
return ans;
}
int main(){
while(~scanf("%d%d",&q,&m)){
if(q==0&&m==0)
break;
memset(g,0,sizeof(g));
for(int i=1;i<=q;i++)
for(int j=1;j<=q;j++){
scanf("%d",&dict[i][j]);
if(dict[i][j]==-1)
dict[i][j]=INF;
}
int d;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&task[i].id,&task[i].x,&d);
task[i].y=task[i].x+d;
}
//floyd算法
for(int k=1;k<=q;k++)
for(int i=1;i<=q;i++)
for(int j=1;j<=q;j++)
if(dict[i][j]>dict[i][k]+dict[k][j])
dict[i][j]=dict[i][k]+dict[k][j];
//构造二分匹配关系图
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(task[j].x>=task[i].y+dict[task[i].id][task[j].id])
g[i][j]=1;
int ans=Hungary();
printf("%d\n",m-ans);
}
}
/*
题意:有A,B两台机器, 机器A 有 n个模式(0, 1, 2....n-1),同样机器B有m个模式, 两个机器一开始的模式都为0,有k个作业(id,x,y) 表示作业编号id, 该作业必须在A机器在模式x下或者B机器在模式y下完成,问你至少要切换几次机器模式。
思路:问题就是问用最少的模式完成所有的任务,每一种模式是一个点,A为一个集合,B为一个集合建立二分图。每一条边就代表这一个任务,现在要用最少的点覆盖所有的边,几最小点集覆盖。
POJ 1325
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 110
int pre[maxn],vis[maxn],dict[maxn][maxn];
bool dfs(int u,int n){
for(int i=1;i<=n;i++){
if(!vis[i]&&dict[u][i]){
vis[i]=1;
if(pre[i]==-1||dfs(pre[i],n)){
pre[i]=u;
return true;
}
}
}
return false;
}
int main(){
int na,nb,t;
while(scanf("%d",&na)&&na){
memset(dict,0,sizeof(dict));
memset(pre,-1,sizeof(pre));
scanf("%d%d",&nb,&t);
while(t--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
dict[y][z]=1;
}
int sum=0,ma=max(na,nb);
for(int i=1;i<=na;i++){
memset(vis,0,sizeof(vis));
if(dfs(i,ma))
sum++;
}
printf("%d\n",sum);
}
}
/*
题意:在一个有向无环图中,从一些顶点出发,能遍历到图上所有点,要求初始选择的顶点数最少且顶点不重复遍历。
思路:
如果从某个顶点开始遍历的过程看成是路径的选择,那么问题就转化为在有向无环图中找最少的不想交的简单路径,这些路径覆盖图中的所有顶点。可见是关于最小路径覆盖的问题。
在有向无环图中,最小路径覆盖数 = 节点数 — 其对应二分图的最大匹配数。
最小路径覆盖它要求原图必须是有向无环图。
POJ 1422
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MAXN =305;
int dict[MAXN][MAXN];
int cx[MAXN],linker[MAXN],n,m;
bool visited[MAXN];
int dfs(int u){
for(int i=1;i<=n;i++){
if(dict[u][i]&&!visited[i]){
visited[i]=true;
if(linker[i]==-1||dfs(linker[i])){
linker[i]=u;
return true;
}
}
}
return false;
}
int main(){
int test;
scanf("%d",&test);
while(test--){
int x,y;
memset(dict,false,sizeof(dict));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
dict[x][y]=true;
}
int sum=0;
memset(linker,-1,sizeof(linker));
for(int i=1;i<=n;i++){
memset(visited,0,sizeof(visited));
if(dfs(i))
sum+=1;
}
printf("%d\n",n-sum);
}
}
/*
POJ 1469
模板题
*/
#include<cstdio>
#include<cstring>
#define N 1000
using namespace std;
int n,p,linker[N],sum;
bool dict[N][N],visited[N];
bool dfs(int a){
for(int i=1;i<=n;i++)
if(dict[a][i]&&!visited[i]){
visited[i]=1;
if(linker[i]==-1||dfs(linker[i])){
linker[i]=a;
return true;
}
}
return false;
}
int main(){
int test,counti,temp;
scanf("%d",&test);
while(test--){
scanf("%d%d",&p,&n);
sum=0;
memset(dict,false,sizeof(dict));
memset(linker,-1,sizeof(linker));
for(int i=1;i<=p;i++){
scanf("%d",&counti);
for(int j=1;j<=counti;j++){
scanf("%d",&temp);
dict[i][temp]=true;
}
}
for(int i=1;i<=p;i++){
memset(visited,0,sizeof(visited));
if(dfs(i))
sum++;
}
if(sum==p)
printf("YES\n");
else
printf("NO\n");
}
}