• [bzoj2547]玩具兵<Spfa+二分+匈牙利算法>


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2547

    挺有意思的一道题,这道题可以划分成几个小题。。。。。。。

    题目大意:

    三个兵种在一个n*m的图上,图的每一个格子有一个高度,三个兵种一种走不下降的路,一种走不上升的路,一种随便走。数量分别是kk1

    然后t个目的地,每个目的地必须有ri个兵,ri之和为2*k+1

    然后你可以交换兵,让不能走的情况又变成能走,例如,第一种兵走到了山顶,四周都是下降的,这时候就可以和第二,三种兵交换位置,继续走下去。。。

    【解题思路】

    这道题分解下来就是先来一个spfa找每一个兵到每一个点的最小交换次数,然后就是二分这个一共的交换次数,接着就是二分图匹配。。。

    我们跑spfa,以每个兵开始扩展,遇到不能走就换一次,记录每个兵到每个点的最小交换次数,然后把当前兵到指定点的值记录入一个新的数组(表示二分图的连接情况)

    然后二分。。因为最少是不交换,最大交换2*k次,因为可以每个兵和天兵交换一次然后去终点,天兵是随便走,所以最多交换2*k次,然后二分次数。。二分当中的check就是用二分图匹配了。。。Check成立的判断时交换次数+最大匹配数,如果这个值大于了兵的数量,说明这个方案是可行的。。。

    这是大致思路,但是还是要注意一些细节的地方。

    Spfa要注意当前的兵种是在变化的,所以要结合到当前位置之前的交换次数和最初状态

    最初为0,交换偶数次,当前还是0,交换奇数次,当前为1

    最初为1,交换偶数次,当前还是1,交换奇数次,当前为0

    假设开始是c,次数是t,这个最终结果就是c^(t&1)

    另外一个就是要注意二分图匹配时,除了要求vis[i]==0之外,还有x兵到i点的次数小于我们二分出来的限制。。。

    然后就还是结合代码理解吧,我看别人的思路都不是很懂,还是结合代码读懂的。。。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<iostream>
      5 #include<cmath>
      6 #include<cstdlib>
      7 #include<queue>
      8 #define maxn 105
      9 using namespace std;
     10 
     11 const int dx[]={0,-1,1,0,0};
     12 const int dy[]={0,0,0,-1,1};
     13 
     14 int att[maxn][maxn],h[maxn][maxn];
     15 int f[maxn][maxn];//表示到达这个位置的最小交换次数 
     16 int n,m,k,t,r;
     17 pair<int,int>a[maxn];
     18 pair<int,int>b[maxn];
     19 queue<pair<int,int> >q;
     20 
     21 void init()
     22 {
     23     scanf("%d%d%d%d",&n,&m,&k,&t);
     24     for(int i=1;i<=2*k+1;i++)
     25     {
     26         scanf("%d%d",&a[i].first,&a[i].second);
     27     }
     28     int tot=1;
     29     for(int i=1;i<=t;i++)
     30     {
     31         scanf("%d%d%d",&b[tot].first,&b[tot].second,&r);
     32         r--;tot++;
     33         for(r;r>=1;r--,tot++){
     34             b[tot]=b[tot-1];
     35         }
     36     }
     37     for(int i=1;i<=n;i++) 
     38      for(int j=1;j<=m;j++)
     39         scanf("%d",&h[i][j]);    
     40 }
     41 
     42 void bfs(int x,int y,int cnt){
     43     memset(f,0x3f3f3f,sizeof(f));
     44     f[x][y]=0;
     45     q.push(make_pair(x,y));
     46     while(!q.empty()){
     47         int nx=q.front().first,ny=q.front().second;    
     48         for(int i=1;i<=4;i++)
     49         {
     50             int nowx=nx+dx[i],nowy=ny+dy[i];
     51             if(nowx<1||nowx>n||nowy<1||nowy>m)continue;
     52             int tt=0;
     53             if(cnt^(f[nx][ny]&1)){
     54                 if(h[nowx][nowy]>h[nx][ny])tt=1;
     55             }else if(h[nowx][nowy]<h[nx][ny])tt=1;
     56             if(f[nowx][nowy]>f[nx][ny]+tt){
     57                 f[nowx][nowy]=f[nx][ny]+tt;
     58                 q.push(make_pair(nowx,nowy));
     59             }
     60             
     61         }
     62         q.pop();    
     63     }
     64     
     65 }
     66 
     67 int vis[maxn*10],ck[maxn*10];
     68 
     69 int ok(int x,int limit){
     70     for(int i=1;i<=2*k+1;i++)
     71     {
     72         if(vis[i]==0&&att[x][i]<=limit){
     73             vis[i]=1;
     74             if(ck[i]==0||ok(ck[i],limit)){
     75                 ck[i]=x;return 1;
     76             }
     77         }
     78     }
     79     return 0;
     80 }
     81 
     82 int check(int x){
     83     memset(ck,0,sizeof(ck));
     84     int tot=0;
     85     for(int i=1;i<=2*k;i++){
     86         memset(vis,0,sizeof(vis));
     87         if(ok(i,x))tot++;
     88     }
     89     if(tot+x>=2*k)return 1;
     90     else return 0;
     91 }
     92 
     93 int ans(){
     94     int l=0,r=2*k;//最坏情况就是天兵挨着和每一个交换,最多换2*k次
     95     while(l<r){
     96         int mid=(l+r)>>1;
     97         if(check(mid))r=mid;else l=mid+1;
     98     } 
     99     return l;
    100 }
    101 
    102 int main()
    103 {
    104     init();
    105     for(int i=1;i<=2*k;i++)
    106     {
    107         if(i<=k)bfs(a[i].first,a[i].second,0);
    108         else bfs(a[i].first,a[i].second,1);
    109         for(int j=1;j<=2*k+1;j++){
    110             att[i][j]=f[b[j].first][b[j].second];
    111         }
    112     }
    113     printf("%d
    ",ans());
    114 }
    View Code

    总结:

    把一个大题打乱成多个小题是一个很有效的方式

    注意分析状态的变化,比如题中的兵种在spfa的时候是变化的

  • 相关阅读:
    RabbitMQ安装(发生系统错误5。拒绝访问。发生系统错误1067。进程意外终止。)
    SQLServer执行脚本提示“系统找不到指定的文件”或“内存资源不足”
    TypeScript@HelloWorld!
    超详细Node安装教程
    进制转换
    菜鸟成长记
    ASP.NET Core中使用MialKit实现邮件发送
    VS未能正确加载 ”Microsoft.VisualStudio.Editor.Implementation.EditorPackate“包错误解决方法
    C#Winfrom Listview数据导入Excel
    安装研发服务器
  • 原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7647673.html
Copyright © 2020-2023  润新知