集训模拟赛-1-T2

好了不要在铺垫了直接整吧就

题目拿来!!!!!!!

倒水
(water)
(256MB,1s)
【问题描述】
你有一个水桶(记为 0),两个杯子(记为 1,2)。水桶中的水量
无限,容量也无限。1 号杯子容量为 a ml,2 号杯子容量为 b ml。一
开始,两个杯子里都没有水。
你可以执行以下几种操作:
1、将 1 号杯子中的水倒入水桶
2、将水桶中的水倒入 2 号杯子
3、将 2 号杯子中的水倒入 1 号杯子
每次倒水直到倒出水的杯子满或倒入水的杯子空才停止。
现在希望 1 号杯子中的水尽量少,但大于 0。
问通过以上操作能够获得 1 号杯子的最少水量 t 为多少?(t>0)
记获得最少水量执行了 pa 次操作 1,pb 次操作 2,希望在最少水量
的条件下 pa 尽量小(在 pa 相同的情况下还希望 pb 尽量小)。

【输入格式】
输入文件名为water.in
一行,两个数正整数 a b
【输出格式】
输出文件名为water.out
一行,三个数 t pa pb
(各变量含义如题所述)
【输入样例】
water.in water.out
5 3 1 1 2

倾倒方案为:
 0->2;
 2->1;
 0->2;
 2->1;
 1->0;
 2->1;
【数据规模与约定】
对于 20%的数据,pa,pb 总和不超过 5
对于 60%的数据,pa<=10^8
对于 100%的数据,0<b<=a<=10^9

说实话当时看完这个题我直接套各种东西= =因为一点思路都没有

但它实际上是个推理题

首先我们要知道每个操作执行时满足的条件

1号杯它不会在没有满时倒出,因为1杯未满说明2杯已空(一定有2->1的情况)若倒了就又重新开始了。

2号杯也只有在空的时候会执行二号操作

而2->1可以看作一步操作,因为水量是一定的,他们不对1号杯最小水量做出影响

把1杯2杯步骤三看作一个整体

杯子容量a+b=x 一开始a,b为空

到了末期一定是a>=0,b=0

体现在代码上:

一号操作:x=x-a

二号:x=x+b

三号:x=x

则求a(min)等于求x(min)

一号操作Pa次,二号操作Pb次,余下的水s=Pb*b-Pa*a

余下a与b的线性组合

x(min)=gcd(a,b)

s=gcd(a,b)

且他们的解还要除一个gcd才是min值的累加

解为-Pa+kb,Pb-ka

最小为 -Pa+kb/gcd(a,b),Pb-ka/gcd(a,b)

(好了我知道你们看不懂= =算了,代码一上估计就好理解多了)

那就代码走起!!!!!!!

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 using namespace std;
 5 int g[71][71];//邻接矩阵 
 6 int f[71][71][71][71];//floyd 
 7 int _Min(int x,int y){return x<y?x:y;}
 8 int main()
 9 {
10     freopen("travel.in","r",stdin);
11     freopen("travel.out","w",stdout);
12     memset(g,63,sizeof(g));
13     memset(f,63,sizeof(f));
14     int n,m,T,x,y,k,i,j,l,r,s,t,L;
15     scanf("%d%d",&n,&m);
16     for(i=1;i<=m;i++)
17     {
18       scanf("%d%d%d",&x,&y,&k);
19       g[x][y]=_Min(g[x][y],k);//保存邻接矩阵中 
20     }
21     for(l=1;l<=n;l++)//f[] 预处理
22       for(r=l;r<=n;r++)//先枚举 l r
23       {
24         for(i=1;i<=n;i++)
25           for(j=1;j<=n;j++)
26           {
27             if(l==r)//只能途经一个点
28               f[l][r][i][j]=_Min(g[i][j],g[i][l]+g[l][j]);//比较
29             else
30               f[l][r][i][j]=_Min(f[l][r-1][i][j],f[l][r-1][i][r]+f[l][r-1][r][j]);//经不经过r 
31           }
32       }
33     scanf("%d",&T);//询问 
34     while(T--)
35     {
36       scanf("%d%d%d",&s,&t,&L);
37       if(g[s][t]<=L){
38       printf("-1\n");//-1为最小的能取到直接出最小答案 
39       continue;
40       }
41       r=1;//分界点右边的节点 恰好小于等于限制条件的那个 
42       int ans=1<<10; 
43       for(l=1;l<=n;l++)
44       {
45         while(r<=n&&f[l][r][s][t]>L)r++;//对于每一行看看是否需要右移 限制在n之内分界点不出去 
46         if(r>n)break;
47         ans=_Min(ans,r-l);//更新答案 
48       }
49       if(ans==1<<10)ans=-2;//没有满足条件,直接出-2 
50       printf("%d\n",ans);
51     }
52     return 0;
53 }

o的k?

奥利给!整他就完了!

好了今天就到这里吧 如有不懂 我也不会讲= =

散会!

posted @ 2020-02-10 22:13  2333333333龖  阅读(176)  评论(0编辑  收藏  举报