算法补充——最大流、二分图、快读、图的遍历

二分图匹配

题目描述

一个N行,M列的棋盘。棋盘每个格子都是边长为1的正方形。
现在要在棋盘上放一些1×2大小的骨牌。骨牌的边线与格子重合(必须占2个格子),任意两个骨牌不能重叠。
但是棋盘上的一些格子已经被占用,请问最多可以放多少个骨牌。

#include 
#include 
#include  
using namespace std; 
const int ms = 105 * 105; 
int n, m; 
vector<int> G[ms]; 
int match[ms]; 
bool visit[ms]; 
bool dfs(int x) 
{ 
 int len = G[x].size();
 for (int i = 0; i < len; ++i) 
 { 
  int to = G[x][i]; 
  if (!visit[to]) 
  { 
   visit[to] = true; 
   if (!match[to] || dfs(match[to])) 
   { 
    match[to] = x; return true; 
   } 
  } 
 }
 return false; 
}
bool p[ms]; 
inline int to1(int a, int b) 
{ 
 return (a - 1)*m + b; 
}
int MaxMatch() 
{ 
 int ans = 0; 
 memset(match, 0, sizeof(match)); 
 for (int i = 1; i <= n; ++i) 
 { 
  for (int j = 1; j <= m; ++j) 
  { 
   if ((i + j) & 1 || p[to1(i, j)]) 
   continue; 
   memset(visit, false, sizeof(visit)); 
   if (dfs(to1(i, j))) ans++; 
  } 
 }
 return ans;
}
int main() 
{ 
 int k; 
 scanf("%d%d%d", &n, &m, &k); 
 int a, b; 
 for (int i = 0; i < k; ++i) 
 { 
  scanf("%d%d", &a, &b); 
  p[to1(a, b)] = true; 
 }
 for (int i = 1; i <= n; ++i) 
 { 
  for (int j = 1; j <= m; ++j) 
  { 
   if (!((i + j) & 1) && !p[to1(i, j)]) 
   { 
    if (i > 1 && !p[to1(i - 1, j)]) 
     G[to1(i, j)].push_back(to1(i - 1, j)); 
    if (j > 1 && !p[to1(i, j - 1)]) 
     G[to1(i, j)].push_back(to1(i, j - 1)); 
    if (i < n && !p[to1(i + 1, j)]) 
     G[to1(i, j)].push_back(to1(i + 1, j)); 
    if (j < m && !p[to1(i, j + 1)]) 
     G[to1(i, j)].push_back(to1(i, j + 1)); 
    } 
  } 
 }
 printf("%d\n", MaxMatch()); 
}

方格取数问题

题目描述

在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

#include
#include
#include
#include
using namespace std;
const int maxn = 1e4+10;
const int inf = 0x3f3f3f3f;
int n,m,s,t,q,tol,head[maxn],dep[maxn],x[1007][1007];
struct Edge
{
    int v,w,nxt;
}E[maxn];
void add_edge(int u,int v,int w)
{
    E[tol] = Edge{v,w,head[u]};
    head[u] = tol++;
}
void insert(int u, int v, int c)
{
    add_edge(u, v, c);
    add_edge(v, u, 0);
}
bool Bfs()
{
    memset(dep,0, sizeof(dep));
    queue<int>q;
    while(!q.empty())
        q.pop();
    q.push(s);
    dep[s] = 1;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = head[u];i != -1;i = E[i].nxt)
        {
            if(E[i].w && !dep[E[i].v])
            {
                dep[E[i].v] = dep[u] + 1;
                q.push(E[i].v);
                if(E[i].v == t)
                    return true;
            }
        }
    }
   return false;
}
int Dfs(int u,int f)
{
    if(u == t)
        return f;
    int used = 0,d = 0;
    for(int i = head[u];i != -1;i = E[i].nxt)
    {
        if(dep[u] == dep[E[i].v] - 1 && E[i].w)
        {
            if((d = Dfs(E[i].v,min(f - used,E[i].w))))
            {
                used += d;
                E[i].w -= d;
                E[i^1].w += d;
            }
        }
    }
    if(!used)
        dep[u] = 0;
    return used;
}
int Dinic()
{
    int max_flow = 0,d;
    while(Bfs())
    {
        while((d = Dfs(s,inf)))
            max_flow += d;
    }
    return max_flow;
}
int main()
{
    memset(head,-1,sizeof(head));
    memset(x,0,sizeof(x));
    scanf("%d%d%d",&n,&m,&q);
    int i,j,sum=0,mm=0;
    t=n*m+1;
    while(q--)
    {
     int a,b;
     scanf("%d%d",&a,&b);
     x[a][b]=-1;
 }
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=m; j++)
        {
            mm++;
            if(x[i][j]==-1) continue; 
            if((i+j)%2)
            {
                insert(0,mm,1);//第一列数与源点相连
                if(i>1&&x[i-1][j]!=-1) insert(mm,mm-n,inf);//连上其上面的数
                if(i<n&&x[i+1][j]!=-1) insert(mm,mm+n,inf);//连上其下面的数
                if(j>1&&x[i][j-1]!=-1) insert(mm,mm-1,inf);//连上其左面的数
                if(j<m&&x[i][j+1]!=-1) insert(mm,mm+1,inf);//连上其右面的数
            }
            else
            {
                insert(mm,t,1);//第二列数与汇点相连
            }
        }
    }
    printf("%d",Dinic());
    return 0;
}

最小割

题目描述

一个无向图,N个点编号1~N。M条边,每条边有一个权值c。
问对于每条边,最少删除多少条边后,可以使得存在一个最小生成树包含这条边。

#include  
#include  
#include  
#include  
#include  
using namespace std; 
const int maxv = 100 + 5; 
const int maxm = 550; 
const int inf = 0x3f3f3f3f; 
struct Edge 
{ 
 int v, e, f; 
};
Edge e[maxm * 2]; 
int head[maxv * 2], depth[maxv * 2]; 
int s, t, cnt = 2; 
queue<int>q; 
inline void addEdge(int from, int to, int val) 
{ 
 e[cnt] = { to,head[from],val }; 
 head[from] = cnt++; 
 e[cnt] = { from,head[to],val }; 
 head[to] = cnt++; 
}
bool bfs() 
{ 
 memset(depth, 0, sizeof(depth)); 
 while (!q.empty()) q.pop(); 
 q.push(s); 
 depth[s] = 1; 
 while (!q.empty()) 
 { 
  int cur = q.front(); 
  q.pop(); 
  for (int i = head[cur]; i; i = e[i].e) 
  { 
   if (!depth[e[i].v] && e[i].f) 
   { 
    depth[e[i].v] = depth[cur] + 1; 
    q.push(e[i].v); 
    if (e[i].v == t) 
    return true; 
   } 
  } 
 }
 return false; 
}
int dfs(int x, int flow) 
{ 
 if (x == t) return flow; 
 int rest = flow; 
 for (int i = head[x]; i && rest; i = e[i].e) 
 { 
  if (depth[x] + 1 == depth[e[i].v] && e[i].f)
  { 
   int f = dfs(e[i].v, min(rest, e[i].f)); 
   if (!f) depth[e[i].v] = 0; 
   e[i].f -= f; 
   e[i ^ 1].f += f; 
   rest -= f; 
  } 
 }
 return flow - rest; 
}
int dinic() 
{ 
 int flow = 0; 
 while (bfs()) 
 { 
  flow += dfs(s, inf); 
 }
 return flow; 
}
Edge ee[maxm]; 
int main() 
{ 
 int n, m, a, b, c; 
 ios::sync_with_stdio(false); cin.tie(nullptr); 
 cin >> n >> m; 
 for (int i = 0; i < m; ++i) 
 { 
  cin >> a >> b >> c; 
  ee[i] = { a,b,c }; 
 }
 for (int i = 0; i < m; ++i) 
 { 
  cnt = 2; 
  memset(head, 0, sizeof(head)); 
  for (int j = 0; j < m; ++j) 
  { 
   if (ee[j].f < ee[i].f) 
   addEdge(ee[j].v, ee[j].e, 1); 
  }
  s = ee[i].v; t = ee[i].e;
  cout << dinic() << " "; 
 }
 return 0; 
}

快速读入

cin和cout关闭和stdio得同流步

iostream::sync_with_stdio(false);

快读

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

快写

inline void write(int x)
{
    char F[200];
    int tmp=x>0?x:-x ;
    if(x<0)putchar('-') ;
    int cnt=0 ;
       while(tmp>0)
       {
           F[cnt++]=tmp%10+'0';
           tmp/=10;
       }
       while(cnt>0)putchar(F[--cnt]) ;
}

dp等差数列

题目描述

现有一数字序列,从中取出一些数字元素,就可以组成一个等差数列,我们想知道这个等差数列最多能有多少个元素,原序列每个元素最多只能取一次。

#include 
#include 
#include 
#define MaxSize 10005
using namespace std;
int n, ans;
int A[MaxSize];
short int dp[MaxSize][MaxSize];
int main()
{
 while(~scanf("%d", &n))
 {
 for (int i = 0; i < n; ++i)
 scanf("%d", &A[i]);
 sort(A, A+n);
 for (int i = 0; i < n; ++i)
 for (int j = i+1; j < n; ++j)
 dp[i][j] = 2;
 ans = 2;
 for (int j = n-2; j > 0; --j) {
 int i = j-1, k = j+1;
 while(i>=0 && k<n)
 {
 if(A[i]+A[k] < 2*A[j])
 k++;
 else if(A[i]+A[k] > 2*A[j])
 i--;
 else
 {
 dp[i][j] = dp[j][k] + 1;
 if(dp[i][j] > ans)
 ans = dp[i][j];
 i--, k++;
 }
 }
 }
 printf("%d\n", ans);
 }
}

dfs遍历

题目描述

从1~n中选取任意多(大于0)个数字,输出所有可能的选择方案一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。方案按照字典序由小到大输出。

#include
#include
bool vis[11];
int answer[11];
int n;
void dfs(int k,int step)
{
	answer[step]=k;
	vis[k]=1;
	for(int i=1;i<=step;i++)
		printf("%d ",answer[i]);
	printf("\n");
	for(int i=k;i<=n;i++)
	{
		if(!vis[i])
		{
			vis[i]=1;
			dfs(i,step+1);
			vis[i]=0;
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		dfs(i,1);
}

题目描述

给定n,m,输出从 1∼n 中选择 m个数的所有排列。
要求按照字典序输出。

//图的去排列 
#include
#include
#include
#include
#include
#include 
using namespace std;
typedef long long ll;
#define maxn 10005 
int n,m;
int ans[10],f[10],vis[10];
void dfs(int k)
{
 if(k==m+1)
 {
  for(int i=1;i<=m;i++)
   printf("%d ",ans[i]);
  printf("\n");
 }
 else
 {
  for(int i=1;i<=n;i++)
  {
   if(!vis[i])
   {
    vis[i]=1;
    ans[k]=f[i];
    dfs(k+1); 
    vis[i]=0;
   }
  }
 }
}
int main()
{
 scanf("%d%d",&n,&m);
 for(int i=1;i<=n;i++)
  f[i]=i;
 dfs(1);
 } 

寻找图有多少条通路(链式前向星)

题目描述

现在给你n个物种和m条能量流动关系,求其中的食物链条数。

物种的名称为从1到n的编号。
m条能量流动关系形如a b 表示能量从物种a 流向物种b。注意单独的一种孤立生物不算一条食物链。

//DFS
#include
#include
#include
#include
#include
#include
typedef long long ll; 
using namespace std;
int n,m;
#define maxn 100010
struct node
{
 int to;
 int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[maxn*2];
int cnt;
int head[maxn];
int out[maxn],in[maxn];
int vis[maxn];
long long sum=0;
void add(int u,int v)
{
  edge[++cnt].to=v;    //edge[i]表示第i条边的终点 
  edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 
  head[u]=cnt;
}
int dfs(int start)
{
 int tot=0;
 if(out[start]==0&&in[start]) 
  return vis[start]=1;
 if(vis[start]) return vis[start];
 for(int i=head[start];i!=0;i=edge[i].next)
 {  
  tot+=dfs(edge[i].to);
 }
 vis[start]=tot;
 return tot;
} 
int main()
{
 memset(head,0,sizeof(head));
 cin>>n>>m;
 int a,b,c;
 while(m--)
 {
  scanf("%d%d",&a,&b);
    add(a,b);//读入表示的是a->b
    out[a]++;in[b]++;
 }
 for(int i=1;i<=n;i++)
 {
  if(in[i]==0&&out[i])
  {
   sum+=dfs(i);
  } 
 }
 printf("%d",sum);
  return 0;
}

你可能感兴趣的:(算法补充——最大流、二分图、快读、图的遍历)