poj Going Home

Going Home

题目:

给出一个N*M的图,图上的m表示人,H表示房子,每座房子只能有一个人,要求你所有人到房子中总步数最少。m个数与H个数一样多。

算法分析:

这个题目还是比较裸的。可以想到先求出每个人到每座房子的距离。然后求出最小花费,这个好像就是最小费用流吧?一开始用了KM写完后,发现。。。。哪里不对啊?后来才觉悟,原来题目是求解最小花费,KM是最大匹配下最大权值。。。。。

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;

typedef pair<int,int> P;
const int INF = 1 << 20;
const int MAXN = 400 + 10;

/////////////////////////////
//最小费用

struct Edge{
    int to,cap,cost,rev;
    Edge(){};
    Edge(int _to,int _cap,int _cost,int _rev)
        :to(_to),cap(_cap),cost(_cost),rev(_rev){};
};

vector<Edge> G[MAXN];
int V;
int h[MAXN];
int dist[MAXN];
int prevv[MAXN],preve[MAXN];

int src,sink;

///////////////////////////////////////

//查找距离
struct Point{
   int x,y,dis;
   Point(){};
   Point(int _x,int _y,int _dis)
        :x(_x),y(_y),dis(_dis){};
};
vector<Point> cost;
map<int,int> mp;
int N,M,cnt1,cnt2;
char str[MAXN][MAXN];
int dirx[5] = {-1,1,0,0};
int diry[5] = {0,0,-1,1};
int W[MAXN][MAXN],ans[MAXN];
int slack,Lx[MAXN],Ly[MAXN],Link[MAXN];
bool S[MAXN],T[MAXN];

////////////////////////////////////

bool check(int x,int y){
   if(x >= 0&& x < N&&y >=0 && y < M)
      return true;
   return false;
}
void bfs(int s,int e){
     bool vst[MAXN][MAXN];
     memset(vst,0,sizeof(vst));
     queue <Point> que;
     que.push(Point(s,e,0));
     vst[s][e] = 1;
     while(!que.empty()){
          Point pv = que.front(); que.pop();
          for(int i = 0;i < 4;++i){
             Point tmp;
             tmp.x = pv.x + dirx[i];
             tmp.y = pv.y + diry[i];
             tmp.dis = pv.dis + 1;
             if(check(tmp.x,tmp.y)&&!vst[tmp.x][tmp.y]){
                vst[tmp.x][tmp.y] = 1;
                if(str[tmp.x][tmp.y] == 'H'){
                    int num = tmp.x * N + tmp.y;
                    if(mp.count(num) == 0){
                        mp[num] = ++cnt2;
                    }
                    cost.push_back(Point(cnt1,mp[num],tmp.dis));
                }
                que.push(tmp);
             }
          }
     }
}
//
//bool Match(int i){
//    S[i] = true;
//    for(int j = 1;j <= cnt2;++j)if(!T[j]){
//        int tmp =   Lx[i] + Ly[j] - W[i][j];
//        if(tmp == 0){
//            T[j] = true;
//            if(Link[j] == -1||Match(Link[j])){
//                Link[j] = i;
//                return true;
//            }
//        }
//        else if(tmp < slack)
//            slack = tmp;
//    }
//    return false;
//}
//
//void update(int d){
//   for(int i = 1;i <= cnt1;++i)
//      if(S[i]) Lx[i] -= d;
//   for(int i = 1;i <= cnt2;++i)
//      if(T[i]) Ly[i] += d;
//}
//
//bool EK(){
//   for(int i = 1;i <= cnt1;++i){
//       Link[i] = -1;
//       Lx[i] = Ly[i] = 0;
//       for(int j = 1;j <= cnt2;++j)
//          Lx[i] = max(Lx[i],W[i][j]);
//   }
//
//   slack = INF;
//
//   for(int i = 1;i <= cnt1;++i){
//      for(;;){
//        memset(S,false,sizeof(S));
//        memset(T,false,sizeof(T));
//        if(Match(i))
//            break;
//
//        if(slack == INF)
//            return false;
//        update(slack);
//      }
//
//   }
//   return true;
//}
//
//int getAns(){
//   int sum = 0;
//   for(int j = 1;j <= cnt2;++j){
//       if(Link[j] != -1)
//         sum += W[Link[j]][j];
//   }
//   return sum;
//}

void addEdge(int from,int to,int cap,int cost){
    G[from].push_back(Edge(to,cap,cost,G[to].size()));
    G[to].push_back(Edge(from,0,-cost,G[from].size() - 1));
}

int min_cost_flow(int s,int t,int f){
   int res = 0;
   V = sink + 1;
   fill(h,h + V,0);
   while(f > 0){
       priority_queue<P,vector<P>,greater<P> > que;
       fill(dist,dist + V,INF);
       dist[s] = 0;
       que.push(P(0,s));
       while(!que.empty()){
           P p = que.top(); que.pop();
           int v = p.second;
           if(dist[v] < p.first) continue;
           for(int i = 0;i < (int)G[v].size();++i){
               Edge& e = G[v][i];
               int tmp = dist[v] + e.cost + h[v] - h[e.to];
               if(e.cap > 0 && dist[e.to] > tmp){
                    dist[e.to] = tmp;
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(P(dist[e.to],e.to));
               }
           }
       }

       if(dist[t] == INF){
           return -1;
       }

       for(int v = 1;v < V;++v) h[v] += dist[v];

       int dd = f;
       for(int v = t;v != s ;v = prevv[v]){
          dd = min(dd,G[prevv[v]][preve[v]].cap);
       }

       f -= dd;
       res += dd * h[t];
       for(int v = t;v != s;v = prevv[v]){
          Edge& e = G[prevv[v]][preve[v]];
          e.cap -= dd;
          G[v][e.rev].cap += dd;
       }
   }
   return res;
}

void init(){
    for(int i = 0;i <= sink;++i)
        G[i].clear();
}

void solve(){
   cnt1 = 0;
   cnt2 = 0;
   mp.clear();
   cost.clear();
   //查找距离
   for(int i = 0;i < N;++i){
     for(int j = 0;j < M;++j){
         if(str[i][j] == 'm'){
            cnt1++;
            bfs(i,j);
         }
     }
   }

   Point tmp;
   for(int i = 0;i < (int)cost.size();++i){
       tmp = cost[i];
       W[tmp.x][tmp.y] = tmp.dis;
   }

   //建图
   V = cnt1 + cnt2;
   src = V + 1; sink = src + 1;
   init();
   for(int i = 1;i <= cnt1;++i){     //m
      for(int j = 1;j <= cnt2;++j){  //H
          addEdge(i,j + cnt1,1,W[i][j]);
      }
   }

   for(int i = 1;i <= cnt1;++i){
       addEdge(src,i,1,0);
   }

   for(int i = 1;i <= cnt2;++i){              //!!!!!!!! 变点的时候一定要注意啊!
      addEdge(cnt1 + i,sink,1,0);
   }

   int res = min_cost_flow(src,sink,cnt1);
   printf("%d\n",res);
}

int main()
{
    //freopen("Input.txt","r",stdin);

    while(scanf("%d%d",&N,&M),(N||M)){
        for(int i = 0;i < N;++i){
            scanf("%s",str[i]);
        }

        solve();
    }
    return 0;
}
时间: 2024-10-23 14:26:32

poj Going Home的相关文章

POJ - 3186 Treats for the Cows (区间DP)

题目链接:http://poj.org/problem?id=3186 题意:给定一组序列,取n次,每次可以取序列最前面的数或最后面的数,第n次出来就乘n,然后求和的最大值. 题解:用dp[i][j]表示i~j区间和的最大值,然后根据这个状态可以从删前和删后转移过来,推出状态转移方程: dp[i][j]=max(dp[i+1][j]+value[i]*k,dp[i][j-1]+value[j]*k) 1 #include <iostream> 2 #include <algorithm&

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

POJ——T2271 Guardian of Decency

http://poj.org/problem?id=2771 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 5932   Accepted: 2463 Description Frank N. Stein is a very conservative high-school teacher. He wants to take some of his students on an excursion, but he is

POJ——T2446 Chessboard

http://poj.org/problem?id=2446 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 18560   Accepted: 5857 Description Alice and Bob often play games on chessboard. One day, Alice draws a board with size M * N. She wants Bob to use a lot of c

poj 1088 滑雪 DP(dfs的记忆化搜索)

题目地址:http://poj.org/problem?id=1088 题目大意:给你一个m*n的矩阵 如果其中一个点高于另一个点 那么就可以从高点向下滑 直到没有可以下滑的时候 就得到一条下滑路径 求最大的下滑路径 分析:因为只能从高峰滑到低峰,无后效性,所以每个点都可以找到自己的最长下滑距离(只与自己高度有关).记忆每个点的最长下滑距离,当有另一个点的下滑路径遇到这个点的时候,直接加上这个点的最长下滑距离. dp递推式是,dp[x][y] = max(dp[x][y],dp[x+1][y]+

POJ 1385 计算几何 多边形重心

链接: http://poj.org/problem?id=1385 题意: 给你一个多边形,求它的重心 题解: 模板题,但是不知道为啥我的结果输出的确是-0.00 -0.00 所以我又写了个 if (ans.x == 0) ans.x = 0 感觉好傻逼 代码: 1 #include <map> 2 #include <set> 3 #include <cmath> 4 #include <queue> 5 #include <stack> 6

POJ 1741 Tree(树的点分治,入门题)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21357   Accepted: 7006 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001).Define dist(u,v)=The min distance between node u and v.Give an in

poj 1655 树的重心

Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13178   Accepted: 5565 Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or m

POJ 2479 Maximum sum

http://poj.org/problem?id=2479 题意: 给出一个整数串,求连续子串1和连续子串2,不相交并且串1加串2的和最大. 思路: 其实就是求最大连续和,题意要求就是求两段最大连续和.我们可以从左边和右边分别求最大连续和,代码中的dp_l[i]就是1~i的最大连续和,dp_r[i]则是i~n的最大连续和. 1 #include<iostream> 2 #include<string> 3 #include<cstring> 4 #include<

POJ 3262 Protecting the flowers

http://poj.org/problem?id=3262 这道题和蝎子那道题是相同贪心思路 http://www.cnblogs.com/oscar-cnblogs/p/6329703.html //贪心方式一:列出函数关系式比较复杂//方式二:像蝎子那道题 先讨论两只 奶牛怎样送是最优的/*a1 b1a2 b2先送1 ans = 2*a1*b2先送2 ans = 2*b1*a2那么如果先送1 更优说明 2*a1*b2 < 2*b1*a2 --->> a1 / b1 < a2