UVa 10603 倒水

题意:有3个有一定容量的杯子,给定一个水的量。问是否能让一个杯子装这个给定容量的水,以及所需要倒来倒去的水的最小量。如果不能,则能倒出的比给定容量小但最接近的量,以及所需要进行倒的水的最小量。

思路:虽然是隐式图搜索的问题,其实和之前数据结构基础章节的图的题很类似,就是一个状态转换,然后深搜或宽搜。只不过这里状态的变换不是像之前那样是固定的,可以用一个多维数组来表示;这里的变换是倒水后三个杯子的容量变化。其实题目中也明确给出了状态的变化,要么某水杯倒完,要么另一水杯被装满。比如a倒向b,可以像下面这样写:

//a to b
    if(hd.sa>0)
    {
      int cha=b-hd.sb;
      if(hd.sa>=cha) newhd={hd.sa-cha,b,hd.sc};
      else newhd={0,hd.sb+hd.sa,hd.sc};
    }
    if(vis[newhd.sa][newhd.sb]==0)
    {
      queue[rear++]=newhd;
    }

但是之后a倒向c等等,与此类似,但又需要重新写一个类似的代码而不能重用这段代码。要重用的话,用一个数组来表示结构体的sa、sb、sc。这样用数组来表示其成员的方法在之前数据结构基础图相关的题中就已经见过,比如迷宫的四个方向等。

但是写完之后通过了网上能找到的所有测试数据,还是WA。。卡了太长时间。最后,这里有所帮助。主要是考虑到两个相同的状态,可能后来出现的状态的倒水量比已经出现过该状态时的倒水量要小,这样的话,虽然该状态已出现过,还是需要把该状态加入队列进行扩展;否则你找到的就不是最小的倒水量。网上大部分的题解应该都是WA了,此题应该后来加强了数据。。因为要找的是最小的到水量,重复状态的处理是一个方面,还有一个方面是bfs进行遍历时找到目标值后并不能终止,因为bfs找的是最少次数的,并不一定是最小倒水量的,必须把所有结点扩展完。

综上,需要注意的点如下:

1.bfs或dfs需要遍历完所有结点,不能提前跳出。

2.重复状态出现时,需要比较两者的倒水量。如果后者的倒水量较小,还是需要重新访问。(有的用hash判重貌似可以;紫书上记录所有解,结束后再找最优解,当然就不存在这个问题了。。)

后面附上一些数据,但是没有检测出第二个注意项。。

Code:

#include<stdio.h>
#include<string.h>

typedef struct node
{
  int s[3];
  int pour;
} stat;

void solve();

int jug[3],d;
stat queue[10000000];
//int pour[10000];
int vis[205][205];//标记状态的a、b值
int visvalue[205][205];//该状态之前访问过的pour值
int closepour,closed;

int main()
{
  //freopen("10603.in","r",stdin);
  //freopen("10603.out","w",stdout);

  int t;
  scanf("%d",&t);
  while(t-->0)
  {
    scanf("%d%d%d%d",&jug[0],&jug[1],&jug[2],&d);
    memset(vis,0,sizeof(vis));
    closepour=closed=0;
    solve();
  }
  return 0;
}

void solve()
{
  int front=0;
  int rear=0;
  //queue[rear++]={{0,0,jug[2]},0};
  stat ys={0,0,jug[2],0};
  queue[rear++]=ys;
  vis[0][0]=1;
  visvalue[0][0]=0;
  int flag=0;//是否找到d值
  while(front<rear)
  {
    stat hd=queue[front++];
    if(hd.s[0]==d || hd.s[1]==d || hd.s[2]==d)
    {
      if(flag==0) { closed=d; closepour=hd.pour; }
      else closepour=hd.pour<closepour?hd.pour:closepour;
      flag=1;
      //break;
    }
    if(flag==0)
      for(int i=0;i<3;++i)
      {
        int tempd=hd.s[i];
        if(tempd<d && tempd>closed) { closed=tempd; closepour=hd.pour;}
        else if(tempd==closed) closepour=hd.pour<closepour?hd.pour:closepour;
      }

    for(int i=0;i<3;++i)//把i往j倒
      for(int j=0;j<3;++j)
      {
        int cha=jug[j]-hd.s[j];//把j填满所需的差值
        if(hd.s[i]<=0 || i==j || cha==0) continue;
        stat newhd=hd;
        if(hd.s[i]>=cha)
        {
          //stat newhd=hd;
          newhd.s[i]=newhd.s[i]-cha;
          newhd.s[j]=jug[j];
          newhd.pour+=cha;
        }
        else
        {
          newhd.pour+=newhd.s[i];
          newhd.s[j]=newhd.s[j]+newhd.s[i];
          newhd.s[i]=0;//注意赋值顺序
        }
        if(!vis[newhd.s[0]][newhd.s[1]] || visvalue[newhd.s[0]][newhd.s[1]]>newhd.pour)
        {
          queue[rear++]=newhd;
          vis[newhd.s[0]][newhd.s[1]]=1;
          visvalue[newhd.s[0]][newhd.s[1]]=newhd.pour;
        }
      }//for

  }//while
  printf("%d %d\n",closepour,closed);
}

Code://最开始写的、没写完的版本,之后改为上面的、用数组代替几个变量、易于代码重用的版本。

#include<stdio.h>

typedef struct node
{
  int sa,sb,sc;
} stat;

int a,b,c,d;
stat queue[10000];
int vis[205][205];//标记状态的a、b值 

int main()
{
  int t;
  scanf("%d",&t);
  while(t-->0)
  {
    scanf("%d%d%d%d",&a,&b,&c,&d);
    memset();
  }
  return 0;
}

void solve(int a,int b,int c)
{
  int front=0;
  int rear=0;
  queue[rear++]={0,0,c};
  vis[0][0]=1;
  while(front<rear)
  {
    stat hd=queue[front++];
    if(hd.sa==d || hd.sb==d || hd.sc==d)
    {

      break;
    }
    stat newhd;
    for(int i=0;i<3;++i)
      for(int j=0;j<3;++j)
      {
        if(i==j) continue;

      }
    //a to b
    if(hd.sa>0)
    {
      int cha=b-hd.sb;
      if(hd.sa>=cha) newhd={hd.sa-cha,b,hd.sc};
      else newhd={0,hd.sb+hd.sa,hd.sc};
    }
    if(vis[newhd.sa][newhd.sb]==0)
    {
      queue[rear++]=newhd;
    }
  }
}

input:

119

33 12 113 6

11 135 70 15

15 153 184 60

184 180 180 146

167 57 83 60

181 32 50 72

85 99 132 34

13 38 109 79

26 53 14 12

143 37 165 83

73 134 189 126

29 34 91 169

23 172 67 173

127 180 181 116

168 188 79 113

175 181 4 164

76 177 11 17

102 19 190 175

125 151 130 179

171 131 161 21

121 170 80 28

151 25 150 61

169 119 13 29

9 121 77 16

40 85 98 88

45 118 80 89

31 170 46 10

12 2 78 7

195 109 148 157

103 146 133 98

138 72 42 145

7 146 192 119

132 23 31 67

97 177 141 88

170 127 124 186

135 158 105 178

127 130 67 30

121 7 53 200

14 36 78 95

91 196 159 47

77 23 19 189

100 151 176 136

78 70 55 100

166 190 28 137

70 94 97 94

22 167 100 150

178 12 4 155

128 69 184 162

116 182 92 3

122 16 133 45

124 199 62 187

159 91 166 89

190 6 161 64

169 188 172 38

137 148 199 175

162 66 193 183

107 55 48 62

144 29 70 157

196 46 5 131

12 132 113 171

88 178 137 74

85 58 166 110

102 199 182 142

38 41 65 175

46 121 96 89

74 166 149 165

101 47 188 153

151 89 179 101

1 17 67 93

151 65 74 10

8 30 64 33

170 22 71 105

108 192 142 166

22 158 158 68

25 99 6 124

54 153 188 161

42 34 169 55

52 169 98 20

43 36 199 138

13 189 58 70

45 120 181 176

40 43 56 116

198 42 118 38

138 29 194 106

182 179 39 163

51 11 148 113

170 70 46 124

34 182 59 80

133 78 80 40

172 172 97 135

98 147 14 15

147 36 152 8

104 106 191 191

41 132 116 116

195 187 2 139

54 29 170 38

138 186 83 27

171 110 159 179

134 69 34 149

153 58 81 185

187 35 164 72

12 23 42 14

12 53 75 23

56 23 34 2

23 55 72 25

124 86 173 84

102 7 109 33

16 116 148 52

121 61 159 140

1 15 17 10

2 13 17 9

14 2 16 11

1 11 13 10

1 7 8 3

0 0 0 0

1 3 6 4

2 3 4 2

96 97 199 62

1 1 1 3

output:

171 6

99 15

120 60

0 0

57 57

0 50

99 33

503 79

0 0

989 81

1850 121

0 91

0 67

127 54

0 79

0 4

0 11

1851 175

0 130

0 0

0 0

100 50

0 13

117 14

490 88

0 80

0 0

12 6

0 148

103 30

0 42

238 119

0 31

97 44

0 124

0 105

0 0

0 53

0 78

0 0

0 19

351 125

0 55

0 28

94 94

0 100

0 4

1199 158

0 0

186 43

0 62

91 75

120 60

169 3

1213 170

426 163

0 48

0 70

0 5

0 113

88 49

7196 110

102 102

0 65

46 50

0 149

1445 153

89 90

0 67

65 9

56 32

0 71

0 142

132 66

0 6

1404 161

42 42

0 0

2279 138

0 58

375 166

0 56

126 34

1480 106

0 39

695 113

0 46

0 59

78 2

0 97

0 14

252 8

0 191

0 116

0 2

705 38

0 0

0 159

0 34

0 81

140 70

166 14

728 23

0 0

23 23

850 77

757 33

176 52

6912 140

13 10

14 9

10 10

5 10

6 3

0 0

3 4

2 2

9859 62

0 1

时间: 2024-10-13 07:26:15

UVa 10603 倒水的相关文章

UVa 10603 倒水问题

https://vjudge.net/problem/UVA-10603 题意:三个杯子,倒水问题.找出最少倒水量. 思路:路径寻找问题.不难,暴力枚举. 1 #include<iostream> 2 #include<queue> 3 #include<string> 4 #include<cstring> 5 using namespace std; 6 7 int a, b, c, d; 8 int vis[205][205]; //访问变量,因为只有

uva 10603 Fill (BFS)

uva 10603 Fill There are three jugs with a volume of a, b and c liters. (a, b, and c are positive integers not greater than 200). The first and the second jug are initially empty, while the third is completely filled with water. It is allowed to pour

UVA 10603 Fill(正确代码虽然很搓,网上许多代码都不能AC)

在做用户查找时 因为要把查找的结果动态加载和显示,所以,那些html元素要由Ajax动态生成.用户打开查找界面时,有系统推荐的用户,而当用户按条件查找后,查找的结果动态加载和显示.所以考虑到用js来搞. 这个for循环就是移除已有的表单.然后根据Ajax请求过来的数据,动态生成新的表单对象.一定要注意j变量从大往小循环,否则,删除div元素后会引起serchResultLenth=serchResult.children.length;长度的变化(这个问题摸索了好久,才搞定,切记) for(va

Fill (Uva 10603 bfs 倒水问题)

题意:三个杯子容量分别为a,b,c,现在c是满的,a和b是空的,两个杯子 i 向 j 倒水,要么 i 倒完了 j 还没满,要么 j 满了 i 还有剩余,问达到某个杯子水量为d时总共倒得最小水量是多少?如果不能达到d,找一个小于d并且离d最近的一个解. 思路:倒水问题,但题目要求的是总的到水量,所以在bfs时到达过的状态还要检查更新,可能当前我确实到达d了用了sum水量,但可能后面还有比sum更小的解.网上有很多代码都是错误的,很多就是错在这里. 代码: #include <iostream>

UVA 10603 Fill倒水问题

There are three jugs with a volume of a, b and c liters. (a, b, and c are positive integers not greater than 200). The ?rst and the second jug are initially empty, while the third is completely ?lled with water. It is allowed to pour water from one j

uva 10603 Fill(倒水问题 BFS)

貌似uva崩了,现在进不去,所以这道题还判断正确与否,其实无所谓了,我这是看的网上的代码,写的基本上一样,唉,没办法,不会做,又看了网上的题解,认真写理解吧还是... 构造了一个结构体,water数组用来保存三个杯子的状态,sum用来保存当前的倒水量,visit数组用来保存状态,以 防他们重复访问,三个杯子只需要两个杯子来判断,第三个已经确定,所以开一个二维数组就可以了...然后用 reach数组来存储所有的倒水量,下标是目标水量,值是一共的倒水量... 只需要一次BFS最多有200*200种状

UVA 10603 Fill(正确代码尽管非常搓,网上很多代码都不能AC)

题目链接:option=com_onlinejudge&Itemid=8&page=show_problem&problem=1544">click here~ 此题我预计是加强过数据,在我纠结了非常久的时候我交了好几份网上的代码不是WA就是TLE.在我非常迷茫的时候我又交了一份,AC了(尽管我用随机数据找到了他代码一个不能过的数据). 给了我信心.然后我拿他的代码用随机数跟我的代码进行測试.再用FC找不同..发现了一个致命的错误.一般来说,BFS或者DFS都是须要

UVa 10603 Fill (暴力BFS+优先队列)

题意:给定4个数,a,b,c,d,分别代表空杯子容积为a,b,一个盛满水的杯子容积为c,让你不断倒水,找一个dd,是不是存在某个时刻, 某个杯子里的水dd,和d相同,或者无限接近.让求最少的倒水量和dd(可能和d相同). 析:首先由于没有刻度,如果用数学方法计算,不好算,样例还好算一点,我们观察那个a,b,c都不大于200,挺小的,适合暴力求解. 就是把所有情况都倒一次,倒水就两种倒法,要么把一个杯子倒满,要么就是这个杯子空了,由于水量是固定的,那么确定两个杯子的水量, 那么第三个也就确定了,所

UVA - 10603 Fill(隐式图搜索)

题目大意:经典的倒水问题.给你三个瓶子,体积为a,b,c. 刚开始a,b是空的,c是满的,现在要求你到出体积为d的水.倒水的规则为,要么倒水方为空,要么接水方满 问倒到容量为d时,倒水的最小体积是多少,如果不能倒出体积为d的水,找出d' < d,最接近d的d'和最小的体积 解题思路:刚才时以为直接bfs,用vis标记一下就结束了,结果WA了.为什么会WA,因为我这样求的是倒水次数最少的,而不是倒水体积最小的,WA是肯定的了 接着将vis数组改成int型的,纪录达到这个状态时倒水的体积,结果可想而