集训模拟赛-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?
奥利给!整他就完了!
好了今天就到这里吧 如有不懂 我也不会讲= =
散会!