zoukankan      html  css  js  c++  java
  • 倒水问题(Fill, UVa 10603)

    【题目描述】

    有三个没有刻度的水壶,容量分别为a,b和c(单位为升,都是<=200的正整数)。初始时前两个水壶是空的,而第三个装满了水。每次可以从一个水壶往一个水壶里倒水,直到一个水壶倒空或者另一个水壶倒满。为了让某个水壶恰好有d升水,至少要倒多少升的水?如果无解,找一个小于且最接近d的d’代替d。

    【输入输出】

    输入

    第一行一个整数 t ,代表有 t 组测试数据,接下来 t 行每行包括 a, b, c, d 四个整数,分别代表三个水壶的容量 a, b, c 和目标水量 d 。

    输出

    输出共 t 行,每一行包括两个整数,分别是最少的倒水量和目标水量(d 或者 d′)。

    【分析】

    假设某一时刻,第一个杯子中有v₁升水,第二个杯子中有v₂升水,第三个杯子中有v₃升水,称此时的状态为(v₁, v₂, v₃)。我们可以把“状态”想成途中的一个结点,通过如何倒水就可以实现状态的转化,那么就可以构成一个状态图。不过提到了图,不代表一定就要建图,因为每一个状态的倒水方式都有最多6种(3² - 3,不能自己往自己倒),那么完全可以求最图上最短路时现算出每一个结点的出边,这种不建图却用到了图论的方法称为隐式图。然后跑最短路的话这里采用 spfa。

    不过先来谈谈建图的方法。对于每一个结点,它的出边可以算出来,那么就可以用 bfs 来建图,然后存图的话就可以用 vector。值得注意的是,三个水壶中的水永远是不变的,恒等于第三个水壶的容量,所以可以用前两个水壶中的水表示第三个水壶中的水,建图就可以从三维降低到二维。

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<vector>
      7 #include<queue>
      8 using namespace std;
      9 const int INF = 2147483647;
     10 const int maxn = 205;
     11 struct Cup    //用结构体比较方便 
     12 {
     13     int wa[3];
     14 };
     15 int wmax[3], d;        //wmax三个杯子容量,d目标水量
     16 bool exist[maxn][maxn];    //本来三维,可用二维表示,判断点是否存在 
     17 vector<Cup>v[maxn][maxn];//同理 
     18 vector<int>c[maxn][maxn]; 
     19 int daoshui(int a, int b, int amax, int bmax, int& newa, int& newb)    //a往b倒 
     20 {
     21     if(a >= bmax - b)    //b被灌满
     22     {
     23         newa = a - (bmax - b); newb = bmax; return bmax - b;
     24     } 
     25     else    //a被倒空
     26     {
     27         newa = 0; newb = b + a; return a; 
     28     } 
     29 }
     30 void init()
     31 {
     32     memset(exist, 0, sizeof(exist));
     33     for (int i = 0; i < maxn; i++)
     34         for(int j = 0; j < maxn; ++j) 
     35         {
     36             v[i][j].clear(); c[i][j].clear();
     37         }
     38     return;
     39 }
     40 void build()        //用bfs建图 
     41 {
     42     init();
     43     exist[0][0] = 1;
     44     queue<Cup>q; q.push((Cup){0, 0, wmax[2]});
     45     while(!q.empty())
     46     {
     47         Cup now = q.front(); q.pop();
     48         for(int i = 0; i < 3; ++i)
     49             for(int j = 0; j < 3; ++j)
     50             {
     51                 if(i == j) continue;    //不能自己往自己倒 
     52                 int t[3]; Cup neww = now;
     53                 int cost = daoshui(now.wa[i], now.wa[j], wmax[i], wmax[j], t[i], t[j]);
     54                 neww.wa[i] = t[i]; neww.wa[j] = t[j];
     55                 if(!exist[neww.wa[0]][neww.wa[1]])    //要进入的这一个状态没被访问过 
     56                 {
     57                     /*在强调一下,之所以开二维,是因为已知前两个杯子里的水,就可以
     58                     求第三个,所以像exist这样的数组,永远是exist[x.wa[0][x.wa[1]],
     59                     而不可能出现exist[x.wa[2]][x.wa[]]。我刚开始wa就是因为写成了
     60                     exist[neww.wa[i]][neww.wa[j]]*/ 
     61                     exist[neww.wa[0]][neww.wa[1]] = 1;
     62                     v[now.wa[0]][now.wa[1]].push_back(neww);//从now这个状态添加一条出边 
     63                     c[now.wa[0]][now.wa[1]].push_back(cost);
     64                     q.push(neww);
     65                 }
     66             }
     67     }
     68     
     69 }
     70 int dis[maxn][maxn], vis[maxn][maxn];
     71 void spfa()
     72 {
     73     for(int i = 0; i < maxn; ++i)
     74         for(int j = 0; j < maxn; ++j)
     75         {
     76             dis[i][j] = INF; vis[i][j] = 0;
     77         }
     78     queue<Cup>q; q.push((Cup){0, 0, wmax[2]});    //初始状态 
     79     dis[0][0] = 0;
     80     while(!q.empty())
     81     {
     82         Cup now = q.front(); q.pop();
     83         vis[now.wa[0]][now.wa[1]] = 0;
     84         for(int i = 0; i < v[now.wa[0]][now.wa[1]].size(); ++i)
     85         {
     86             Cup neww = v[now.wa[0]][now.wa[1]][i];
     87             if(dis[now.wa[0]][now.wa[1]] + c[now.wa[0]][now.wa[1]][i] < dis[neww.wa[0]][neww.wa[1]])
     88             {    //    有更好的结果就更新 
     89                 dis[neww.wa[0]][neww.wa[1]] = dis[now.wa[0]][now.wa[1]] + c[now.wa[0]][now.wa[1]][i];
     90                 if(!vis[neww.wa[0]][neww.wa[1]])
     91                 {
     92                     vis[neww.wa[0]][neww.wa[1]] = 1;
     93                     q.push(neww);
     94                 }
     95             }
     96         }
     97     }
     98     int ans1 = 0, ans2 = 0;
     99     for(int i = 0; i < maxn; ++i)
    100         for(int j = 0; j < maxn; ++j)
    101         {
    102             if(exist[i][j])        //这个点存在 
    103             {
    104                 int newd = 0;
    105                 if(i <= d && i >= newd) newd = i;    //不断更新d′,使 d′不断趋近于 d。 
    106                 if(j <= d && j >= newd) newd = j;
    107                 int cwa = wmax[2] - i - j;
    108                 if(cwa <= d && cwa >= newd) newd = cwa;
    109                 if(newd > ans1 || (newd == ans1 && dis[i][j] < ans2))
    110                 {
    111                     ans1 = newd; ans2 = dis[i][j];
    112                 }
    113             }
    114         
    115         }
    116     printf("%d %d
    ", ans2, ans1);
    117 }
    118 
    119 int main()
    120 {
    121     int t; scanf("%d", &t);
    122     while(t--)
    123     {
    124         
    125         scanf("%d%d%d%d", &wmax[0], &wmax[1], &wmax[2], &d);
    126         build();
    127         spfa();
    128     }
    129     return 0;
    130 }

    代码不短,不过要是改成隐式图搜索,建图的函数几乎就可以全省了。

    那么怎么改呢?想一想建图只不过就是求出了 vector<Cup>v[maxn][maxn] 和 vector<Cup>c[maxn][maxn],那么我们只要将 spfa中的 vector<Cup>v[maxn][maxn] 和 vector<Cup>c[maxn][maxn]替换成找出边和求边权的语句不就行了吗?

    看看上面的代码,这个语句就是

     1 int daoshui(int a, int b, int amax, int bmax, int& newa, int& newb)     
     2 {
     3     if(a >= bmax - b)    
     4     {
     5         newa = a - (bmax - b); newb = bmax; return bmax - b;
     6     } 
     7     else
     8     {
     9         newa = 0; newb = b + a; return a; 
    10     } 
    11 }
    12 
    13 for(int i = 0; i < 3; ++i)
    14             for(int j = 0; j < 3; ++j)
    15             {
    16                 if(i == j) continue;    
    17                 int t[3]; Cup neww = now;
    18                 int cost = daoshui(now.wa[i], now.wa[j], wmax[i], wmax[j], t[i], t[j]);
    19             } 

    那么就可以改了

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 205;
     4 const int INF = 0x3f3f3f3f;
     5 struct Point
     6 {
     7     int w[3];
     8 };
     9 int wmax[3], d;
    10 int pour(int a, int b, int amax, int bmax, int& na, int& nb){ // a -> b
    11     if (bmax - b >= a) {na = 0, nb = b + a; return a;}
    12     else {na = a - (bmax - b), nb = bmax; return bmax - b;}
    13 }
    14 int dist[maxn][maxn];
    15 int vis[maxn][maxn];
    16 void spfa(){
    17     queue<Point>q; q.push((Point){0, 0, wmax[2]});
    18     for (int i = 0; i < maxn; i++)
    19         for (int j = 0; j < maxn; j++) dist[i][j] = INF;
    20     dist[0][0] = 0;
    21     while (!q.empty()){
    22         Point now = q.front(); q.pop(); vis[now.w[0]][now.w[1]] = 0;
    23         for (int i = 0; i < 3; i++){
    24             for (int j = 0; j < 3; j++){
    25                 if (i == j) continue;
    26                 int t[3];
    27                 Point np = now;
    28                 int cost = pour(np.w[i], np.w[j], wmax[i], wmax[j], t[i], t[j]);
    29                 np.w[i] = t[i], np.w[j] = t[j];
    30                 if (dist[np.w[0]][np.w[1]] > dist[now.w[0]][now.w[1]] + cost){
    31                     dist[np.w[0]][np.w[1]] = dist[now.w[0]][now.w[1]] + cost;
    32                     if (!vis[np.w[0]][np.w[1]]){
    33                         vis[np.w[0]][np.w[1]] = 1;
    34                         q.push(np);
    35                     }
    36                 }
    37             }
    38         }
    39     }
    40     int ans1 = 0, ans2 = 0;
    41     for (int i = 0; i < maxn; i++){
    42         for (int j = 0; j < maxn; j++){
    43             if (dist[i][j] < INF){
    44                 int nd = 0;
    45                 if (i <= d && i >= nd) nd = i;
    46                 if (j <= d && j >= nd) nd = j;
    47                 int k = wmax[2] - i - j;
    48                 if (k <= d && k >= nd) nd = k;
    49                 if (nd > ans1 || (nd == ans1 && dist[i][j] < ans2)) {ans1 = nd; ans2 = dist[i][j];}
    50             }
    51         }
    52     }
    53     printf("%d %d
    ", ans2, ans1);
    54 }
    55 int main()
    56 {
    57     int t; scanf("%d", &t);
    58     while(t--)
    59     {
    60         scanf("%d%d%d%d", &wmax[0], &wmax[1], &wmax[2], &d);
    61         spfa();
    62     }
    63     return 0;
    64 }

    代码量骤减。。

  • 相关阅读:
    Odoo电子数据交换(EDI)
    WMS8_仓库配置
    odoo写邮件添加收件人
    Odoo HR Payslip
    openERP邮件(发信、收信)
    Multi-company rules
    MRP Force Reservation的作用
    使用虚拟机VM运行Linux版OpenERP
    采购预付款
    消除递归的方法
  • 原文地址:https://www.cnblogs.com/mrclr/p/8391433.html
Copyright © 2011-2022 走看看