考试 - 2017.10.7

T1

地震

描述

一场地震毁了Farmer John的整个农场。他是个有恒心的人,决定重建农场。在重建了所有n(1<=n<=400)块田野后,他意识到还得修路将它们连起来。完工后,任两个田野间必须有路。
研究了地形后,FJ认为m(1<=m<=10000)条双向的道路可能建造。由于资金短缺,他希望已尽可能省钱的方式完成整个工程。
幸运的是,奶牛们已经成立了针对地震后修建农场道路的工程顾问公司。奶牛们也很有经济头脑,对没有漂亮利润的工作从不感兴趣。
奶牛们关心可能的利益。他们已经说定了为修路所获的酬金f(1<=f<=2,000,000,000),并得到一张关于可能的道路、修建每条路的时间(以小时计)(1<=t<=2,000,000,000)以及花费(1<=c<=2000,000,000)的列表。在两块田野间可能有多于一条的道路被列出,所给数据总有可以连通所有田野的修路方案,虽然可能无利可图。
确定奶牛修路最高的盈利率。

输入

第一行三个整数N, M, F。
2...M+1行: 每行四个空格隔开的整数: i, j, c,t描述两块田夜间的一条道路。

输出

只包含一个数,保留四位小数,奶牛每个小时可以得到的最大利润,如果利润非正,输出0.0000 。

样例

quake.in
5 5 100
1 2 20 5
1 3 20 5
1 4 20 5
1 5 20 5
2 3 23 1
quake.out
1.0625
*奶牛选择建最后四条路,总花费83时间16,他们的筹劳是100,所以他们在16各单位时间内得到利润100-83:17/16 = 1.0625。

和最小生成树有点像
不如叫它平均最小生成树
题目要求的是 $ \frac { F- \sum c_i }{ \sum t_i } = $最大值 $ ans $,
通过瞎移可得到 $ \sum t_i \times ans = F - \sum c_i $
二分ans,把边权设成 $ c_i + t_i \times ans $,跑最小生成树就好了。
代码蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
    register int _a=0;bool _b=1;register char _c=getchar();
    while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
    while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
    return _b?_a:-_a;
}
const int _ = 402,__ = 10002;
struct edge
{
	int to,fr,len,tim;double co;
	edge(){to=fr=len=tim=co=0;}
	const bool operator < (const edge &b)const{return co<b.co;}
}e[__];
int ecnt=0;
void add(int fr,int to,int len,int tim)
{e[++ecnt].to=to,e[ecnt].len=len,e[ecnt].fr=fr,e[ecnt].tim=tim;}
int fa[_],n,m,got;
int finder(int a){return a!=fa[a]?fa[a]=finder(fa[a]):a;}
void link(int a,int b){fa[finder(a)]=finder(b);}
bool kruskal(double tar)
{
	register int i,j=1;register double s=got;
	for(i=1;i<=ecnt;i++)e[i].co=tar*e[i].tim+e[i].len,printf("%d %lf\n",i,e[i].co);
	sort(e+1,e+ecnt+1);
	for(i=1;i<=n;i++)fa[i]=i;
	for(i=1;i<=m;i++)
	{
		if(finder(e[i].fr)!=finder(e[i].to))
		{
			link(e[i].fr,e[i].to);
			s-=e[i].co;
			if(s<0)return 0;
		}
	}
	return 1;
}
int main()
{
	register int i,j,k,a,b;register double l=0,r=1e29;
	n=gotcha(),m=gotcha(),got=gotcha();
	for(i=1;i<=m;i++)
	{
		j=gotcha(),k=gotcha(),a=gotcha(),b=gotcha();
		add(j,k,a,b);
	}
	if(!kruskal(0)){puts("0.0000");return 0;}
	while(fabs(l-r)>=1e-13)
	{
		double mid=(l+r)/2;
		if(kruskal(mid))l=mid;
		else r=mid;
		cerr<<l<<" "<<r<<endl;
	}
	printf("%.4lf",l);
	return 0;
}

T2

http://www.lydsy.com/JudgeOnline/problem.php?id=1816 .

扑克牌

描述

你有n种牌,第i种牌的数目为ci。另外有一种特殊的牌:joker,它的数目是m。你可以用每种牌各一张来组成一套牌,也可以用一张joker和除了某一种牌以外的其他牌各一张组成1套牌。比如,当n=3时,一共有4种合法的套牌:{1,2,3}, {J,2,3}, {1,J,3}, {1,2,J}。
给出n, m和ci,你的任务是组成尽量多的套牌。每张牌最多只能用在一副套牌里(可以有牌不使用)。

输入

第一行包含两个整数n, m,即牌的种数和joker的个数。第二行包含n个整数ci,即每种牌的张数。

输出

输出仅一个整数,即最多组成的套牌数目。

样例

cards.in
3 4
1 2 3
cards.out
3

解释

输入数据表明:一共有1个1,2个2,3个3,4个joker。最多可以组成三副套牌:{1,J,3}, {J,2,3}, {J,2,3},joker还剩一个,其余牌全部用完。

数据范围

50%的数据满足:2<=n<=5, 0<=m<=106, 0<=ci<=200
100%的数据满足:2<=n<=50, 0<=m, ci<=500,000,000。


一道二分。
二分答案ans,要加的joker的数量就是 $ \sum max(mid-cnt[i],0) $.
要判断是否超出了拥有的joker数量。
代码蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
    register int _a=0;bool _b=1;register char _c=getchar();
    while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
    while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
    return _b?_a:-_a;
}
const int _ = 52;
int cnt[_],n,m;
bool finder(int a)
{
	register int i,j=min(a,m);
	for(i=1;i<=n;i++){if(cnt[i]<a)j-=a-cnt[i];if(j<0)return 0;}return 1;
}
int main()
{
	register int i,mid,l=0,r=2e9;
	n=gotcha(),m=gotcha();
	for(i=1;i<=n;i++)cnt[i]=gotcha();
	while(r>l){mid=(l+r+1)>>1;if(finder(mid))l=mid;else r=mid-1;}
	printf("%d",l);
	return 0;
}

T3

https://www.luogu.org/problem/show?pid=3199#sub .

请使用死八法判负环的方法来作这一题
二分答案 $ ans $ ,在死八法的时候把每一条边权都减掉 $ ans $ ,
如果找到了负环就说明 $ ans $ 大了,反之 $ ans $ 小了。
最后 $ ans $ 的值就是最小圈的平均边权值了

代码蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
	register int _a=0;bool _b=1;register char _c=getchar();
	while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
	while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
	return _b?_a:-_a;
}
const int _ = 3002, __ = 10002;
struct edge{int to,ne,len;edge(){to=ne=len=0;}}e[__];
int he[_]={0},ecnt=0;
void add(int fr,int to,int len)
{e[++ecnt].to=to,e[ecnt].len=len,e[ecnt].ne=he[fr],he[fr]=ecnt;}
int n,m;
double dis[_],mid;
bool ed[_],vict;
void spfa(int d)
{
	if(vict)return;
	ed[d]=1;int i,a;
	for(i=he[d];i;i=e[i].ne)
	{
		a=e[i].to;if(dis[a]>dis[d]+e[i].len-mid)
		{dis[a]=dis[d]+e[i].len-mid;if(ed[a]){vict=1;return;}spfa(a);}
	}
	ed[d]=0;
}
bool check()
{
	memset(dis,0,sizeof(dis)),memset(ed,0,sizeof(ed)),vict=0;
	for(int i=1;i<=n;i++){spfa(i);if(vict)return 1;}return 0;
}
int main()
{
	register int i,j,k,a;
	register double l=-1e33,r=1e33;
	n=gotcha(),m=gotcha();
	for(i=1;i<=m;i++)j=gotcha(),k=gotcha(),a=gotcha(),add(j,k,a);
	while(l+1e-10<r){mid=(l+r)/2;if(check())r=mid;else l=mid;}
	printf("%.8lf",l);
	return 0;
}

posted @ 2017-10-09 11:55  iot;  阅读(210)  评论(0编辑  收藏  举报
知识共享许可协议
年轻人,你需要更多的知识