• [luogu]P2825 [HEOI2016/TJOI2016]游戏


    原题链接:P2825 [HEOI2016/TJOI2016]游戏

    题意

    就是在一张图上放炸弹,炸弹占领横纵一行一列。

    炸弹不能穿透硬石头,软硬石头上都不能放炸弹。

    求最多能放几个炸弹。

    分析

    (第一道没看题解AC的网络流)

    感觉跟这道题有点像啊。。

    P1129 [ZJOI2007]矩阵游戏

    如果没有硬石头的限制,这道题就是要求行和列的匹配。

    如果加了硬石头怎么办?

    一个位置,它能覆盖到的行应该是往上下一直延伸到硬石头,往左右一直延伸到硬石头。

    我们可以把行和列重分块(长或宽为$1$),一个块内只能有空格和软石头,然后就是块和块的匹配了。

    一个位置可以匹配它横向和纵向的两个块,我们从横向往纵向连边。

    然后源点向所有的横向连边,所有的纵向往汇点连边,跑一遍网络流版二分图匹配就可以了。

    代码

      1 #include <bits/stdc++.h>
      2 #define ll long long
      3 #define Mid ((l+r)>>1)
      4 #define ull unsigned long long
      5 using namespace std;
      6 const int N=59,M=1e5+1009;
      7 int read(){
      8     char c;int num,f=1;
      9     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
     10     while(c=getchar(), isdigit(c))num=num*10+c-'0';
     11     return f*num;
     12 }
     13 int readc(){
     14     char c=getchar();
     15     while(c!='#'&&c!='*'&&c!='x')c=getchar();
     16     if(c=='#')return 2;
     17     if(c=='*')return 1;
     18     if(c=='x')return 0;
     19 }
     20 int n,m,g[N][N],cnt1,cnt2;
     21 int L[N][N],R[N][N],dis[M],s,t;
     22 int head[M],nxt[M],ver[M],edge[M],tot=1;
     23 queue<int>q;
     24 void add_edge(int u,int v,int w){
     25     //cout<<u<<" "<<v<<endl;
     26     ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;edge[tot]=w;
     27     ver[++tot]=u;nxt[tot]=head[v];head[v]=tot;edge[tot]=0;
     28 }
     29 bool bfs(){
     30     while(q.size())q.pop();q.push(s);
     31     memset(dis,0,sizeof(dis));dis[s]=1;
     32     while(q.size()){
     33         int x=q.front();q.pop();
     34         for(int i=head[x];i;i=nxt[i]){
     35             if(dis[ver[i]]||!edge[i])continue;
     36             dis[ver[i]]=dis[x]+1;
     37             q.push(ver[i]);
     38             if(ver[i]==t)return 1;
     39         }
     40     }
     41     return 0;
     42 }
     43 int dinic(int x,int flow){
     44     if(x==t)return flow;
     45     int res=flow,y,k;
     46     for(int i=head[x];i&&res;i=nxt[i]){
     47         y=ver[i];
     48         if(dis[y]!=dis[x]+1||!edge[i])continue;
     49         k=dinic(y,min(res,edge[i]));
     50         if(!t)dis[y]=0;
     51         edge[i]-=k;edge[i^1]+=k;
     52         res-=k;
     53     }
     54     return flow-res;
     55 }
     56 int main()
     57 {
     58     //freopen("4.in","r",stdin);
     59     //freopen("data.out","w",stdout);
     60     n=read();m=read();
     61     for(int i=1;i<=n;i++)
     62         for(int j=1;j<=m;j++)
     63             g[i][j]=readc();
     64     for(int i=1;i<=n;i++){
     65         for(int j=1;j<=m;j++){
     66             if(g[i][j]==2||L[i][j])continue;
     67             cnt1++;
     68             for(int k=j;k<=m&&g[i][k]!=2;k++)
     69                 L[i][k]=cnt1;
     70         }
     71     }
     72     int t1=cnt1;
     73     //编号行 
     74     /*
     75     cout<<endl;
     76     for(int i=1;i<=n;i++){
     77         for(int j=1;j<=m;j++)
     78             cout<<L[i][j]<<" ";
     79         cout<<endl;
     80     }
     81     cout<<endl;
     82     */
     83     for(int j=1;j<=m;j++){
     84         for(int i=1;i<=n;i++){
     85             if(g[i][j]==2||R[i][j])continue;
     86             cnt1++;
     87             for(int k=i;k<=n&&g[k][j]!=2;k++)
     88                 R[k][j]=cnt1;
     89         }
     90     }
     91     //编号列
     92     /*
     93      cout<<endl;
     94     for(int i=1;i<=n;i++){
     95         for(int j=1;j<=m;j++)
     96             cout<<R[i][j]<<" ";
     97         cout<<endl;
     98     }
     99     cout<<endl;
    100     */
    101     //重标号
    102     s=cnt1+11;t=cnt1+15;
    103     for(int i=1;i<=t1;i++)
    104         add_edge(s,i,1);
    105     for(int i=t1+1;i<=cnt1;i++)
    106         add_edge(i,t,1);
    107     for(int i=1;i<=n;i++)
    108         for(int j=1;j<=m;j++){
    109             //横号竖向连接
    110             if(g[i][j]!=1)continue;
    111             //cout<<L[i][j]<<" "<<R[i][j]<<endl;
    112             add_edge(L[i][j],R[i][j],1);
    113         }
    114     //连边
    115     /*
    116     for(int i=head[7];i;i=nxt[i])
    117         cout<<ver[i]<<" ";
    118     cout<<endl;
    119     */
    120     int maxflow=0,flow;
    121     while(bfs())while(flow=dinic(s,(1<<31)-1))maxflow+=flow;    
    122     printf("%d
    ",maxflow); 
    123     return 0;
    124 }
    125 /*
    126 由于有硬石头和软石头,就不能当做普通的二分图匹配跑了
    127 对于一个石头分隔开的一段,我们完全可以把它当成两段匹配
    128 可以匹配的行数和列数不一样
    129 具体来说,可以按照硬石头的位置,把一段行和一段列拆开,重标号
    130 软石头是不能放的
    131 所以在连边的时候注意 
    132 然后跑匹配就可以了。
    133 
    134 */ 
    135 
    136 
    137 
    138 
    139 
    140 
    141 
    142 
    143 
    144 
    145 
    146  
    View Code
  • 相关阅读:
    C# 关于调用微信接口的代码
    Winform实现微信功能
    Delphi 7 在IDE菜单中增加UPX工具
    Delphi7中Unicode,ANSI,UTF编码问题
    本机时间不正确带来的问题
    Delphi最简化异步选择TCP服务器
    C# Winform小程序:局域网设置NTP服务器、实现时间同步
    开源Inno Setup官网下载、安装、打包教程(官网安装向导中文语言包)
    css 雪碧图的制作
    原生js 实现的瀑布流
  • 原文地址:https://www.cnblogs.com/onglublog/p/10388207.html
Copyright © 2020-2023  润新知