hdu 3551 Hard Problem 一般图最大匹配+给出一个无向图,存在重边,没有自环。问能否删除一些边,使得每个顶点的度数为指定度数

本文探讨了一个图论问题,即如何通过删除无向图中的边,使每个顶点的度数达到指定值。通过预处理每个顶点的度数,如果度数等于已连接的边数,该边不能删除。当两个顶点的度数大于目标值时,它们之间的边可以删除。通过将节点拆分并建立新的图,求解一般图的最大匹配,如果存在完美匹配,则表示可以达到目标状态,输出“YES”,否则输出“NO”。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description
This is the most tough task in this contest, do not try it until you solve all the other tasks or you feel boring on others. Given an undirected graph, you are to find out a subgraph of it so that the degree of the i-th node in the subgraph is the given integer D i. The subgraph is a subset of edges and all vertexes are reserved. Notice that the graph may be disconnected, and two edges may connect the same vertexes, but no self cyclic exists.
 


 

Input
The input contains several test cases, the first line of the input contains an integer T denoting the number of test cases.
For each test case, the first line contains two integers N and M, denoting the number of vertexes and number of edges in the graph. (1 <= N <= 50, 1 <= M <= 200)
For the next M lines, each line contains two integers X and Y, denote there is a edge between X-th node and Y-th node. (1 <= X, Y <= N)
For the last N lines, each line contains a single integer D i, denote the degree of i-th node in the subgraph.
 


 

Output
For each test case, if the subgraph exist, output "YES", otherwise output "NO". See sample output for further details
 


 

Sample Input
  
  
2 4 4 1 2 3 4 2 3 1 4 1 2 1 0 4 5 2 1 1 2 2 3 3 4 3 4 1 0 1 0
 


 

Sample Output
  
  
Case 1: YES Case 2: NO

 

 //

 我们应该删除哪些边呢? 预处理每个顶点的度数d[i], 若d[i] = deg[i], 那么 与这个点相连的边是不能删掉的。原因很显然。若i与j之间有边,并且d[i]>deg[i], d[j]>deg[j]那么这条边是可以删除的。接下来如何建图呢? 将每个点i 拆成 d[i] - deg[i]个点。如果i与j之间的边e可以删除, 则边e与i、j拆出的每个点连一条边 ei, ej(重边连多次)。然后求该一般图最大匹配,若存在完美匹配,则YES。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 800;
int n,head,tail,start,finish,match[MAXN],fa[MAXN],base[MAXN],Q[MAXN];//match初始为0 n个点的图 从1开始
int adj[MAXN][MAXN];   //邻接矩阵
bool mark[MAXN],in_blossom[MAXN],in_queue[MAXN];
inline void Contract (int x,int y){
  memset(mark,0,sizeof(mark));
  memset(in_blossom,0,sizeof(in_blossom));
#define pre fa[match[i]]
  int lca,i;
  for(i = x;i;i = pre){
    i = base[i];
    mark[i] = 1;
  }
  for(i = y;i; i = pre){
    i = base[i];
    if(mark[i]){
      lca = i;
      break;
    }
  }
  for (i = x; base[i] != lca; i = pre){
    if(base[pre] != lca) fa[pre] = match[i];
    in_blossom[base[i]] = 1;
    in_blossom[base[match[i]]] = 1;
  }
  for (i = y; base[i] != lca; i = pre){
    if (base[pre] != lca) fa[pre] = match[i];
    in_blossom[base[i]] = 1;
    in_blossom[base[match[i]]] = 1;
  }
#undef pre
  if (base[x] != lca) fa[x] = y;
  if (base[y] != lca) fa[y] = x;
  for (i = 1; i <= n; ++i){
    if (in_blossom[base[i]]){
      base[i] = lca;
      if (!in_queue[i]){
        Q[++tail] = i;
        in_queue[i] = 1;
      }
    }
  }
}
inline void Change(){
  int x,y,z;
  z = finish;
  while (z){
    y = fa[z];
    x = match[y];
    match[y] = z;
    match[z] = y;
    z = x;
  }
}
inline void FindAugmentPath(){
  memset(fa,0,sizeof(fa));
  memset(in_queue,0,sizeof(in_queue));
  for(int i = 1; i <= n; ++i)base[i] = i;
  head = 0; tail = 1;
  Q[1] = start;
  in_queue[start] = 1;
  while (head != tail){
    int x = Q[++head];
    for (int y = 1; y <= n; ++y){
      if (adj[x][y] && base[x] != base[y] && match[x] != y)
        if (start == y || match[y] && fa[match[y]])
          Contract(x,y);
        else if(!fa[y]){
          fa[y] = x;
          if(match[y]){
            Q[++tail] = match[y];
            in_queue[match[y]] = 1;
          }
          else {
            finish = y;
            Change();
            return;
          }
        }
    }
  }
}
inline void Edmonds(){
  memset(match,0,sizeof(match));
  for (start = 1; start <= n; ++start)
    if (match[start] == 0)
      FindAugmentPath();
}
inline void init(){
 memset(adj,0,sizeof(adj));
}
int deg[MAXN], D[MAXN], M;
pair<int ,int> edge[MAXN], id[MAXN];
int main(){
  int Case, u, v,V;
  scanf("%d",&Case);
  for(int it = 1;it <= Case; ++it){
    scanf("%d%d",&V,&M);
    memset(deg, 0, sizeof(deg));
    memset(adj, 0, sizeof(adj));
    for(int i = 0;i < M; ++i){
      scanf("%d%d",&u,&v);
      u --; v --;
      edge[i] = make_pair(u, v);
      deg[u] ++; deg[v] ++;
    }
    for(int i = 0;i < V; ++i){
      scanf("%d",&D[i]);
    }
    bool flag = true;
    int cnt = 1;
    for(int i = 0;i < MAXN; ++i) id[i] = make_pair(-1, -1);
    for(int i = 0;i < V; ++i)
      if(deg[i] < D[i]) { flag = false; break; }
    printf("Case %d: ",it);
    if(!flag) {
      puts("NO");
      continue;
    }
    for(int i = 0;i < M; ++i){
      u = edge[i].first;
      v = edge[i].second;
      if(id[u].first == -1){
        id[u] = make_pair(cnt, cnt + deg[u] - D[u] - 1);
        cnt += (deg[u] - D[u]);
      }
      if(id[v].first == -1){
        id[v] = make_pair(cnt, cnt + deg[v] - D[v] - 1);
        cnt += (deg[v] - D[v]);
      }
      if(id[V+i].first == -1){
        id[V+i] = make_pair(cnt, cnt + 1);
        cnt += 2;
      }
      int t = id[V+i].first;
      adj[t][t+1] = adj[t+1][t] = true;//important
      for(int j = id[u].first;j <= id[u].second; ++j)
        adj[t][j] = adj[j][t] = true;
      for(int j = id[v].first;j <= id[v].second; ++j)
        adj[t+1][j] = adj[j][t+1] = true;
    }
    int j, sum = 0;
    n  = cnt-1;
    flag = 1;
    Edmonds();
    for(int i = 1; i <= n; ++i){
      if(!match[i]){
        flag = 0;
        break;
      }
    }
    if(flag) puts("YES");
    else puts("NO");
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值