NOIP2017模拟赛(二)总结

NOIP2017模拟赛(二)解题报告:

其实这是一份假的解题报告,因为我根本没有参加模拟赛。Ab_ever他们比赛的时候我还在家里睡午觉……(虽然我事后很快就把题目改到了满分)

T1:

星星光芒

题目描述

给你一个N个结点的有向图,而且给你一个N * N的邻接矩阵,表示两个结点之间是否有边。star是这样定义的 : 它有一个中心结点,并且中心结点至少有3个出度,出度用于计算star的光芒程度。 对于一个结点V来说,它可以有多颗star, 记为结点V的starnumber. 例如, 如果结点V 的出度是5, 那么结点V的 starnumber 通过计算等于16, 因为以V为中心结点而且光芒程度是3的有C(5,3) =10颗star, 光芒程度是4 的有C(5,4)=5颗star,光芒程度是5 的有C(5,5)=1颗star. 所以,结点V共有10 + 5 + 1 = 16颗不同的star。如果一个结点的出度是X,且X>=3,那么该结点共有:C(X,3) + C(X,4) + C(X,5) + ...+ C(X,X)颗不同的star。上面提到的C其实就是组合数。
下面我们定义有向图中的全明星路径:如果某条路径同时满足下面两个条件,则认为是全明星路径: 
(1) 路径上的任意一个结点的star number大于0且不超过给定的整数G. 
(2) 路径上的结点 Vi 和 Vi+1,要保证Vi+1 的star number不小于Vi 的star number。 
你的任务是:计算给出的有向图,最长的全名星路径有多少个结点。如果全明星路径可以无限长,输出-1.如果没有全明星路径 (也就是给出的图中的所有结点的star number要么是0,要么大于G),则输出 0。

输入格式 1852.in

多组测试数据。
第一行:一个整数ng, 1 <= ng <= 5. 表示有ng组测试数据。
每组测试数据格式如下:
第一行:两个整数N、C. 2 <= N <=50, 1 <= G <= 10^9.
接下来有N行,每行有N列,表示邻接矩阵,如果有边则是1,否则是0。保证第i行的第i个是0.

输出格式 1852.out

最长的全名星路径有多少个结点。
ng
行,每行对应一组输入数据。

输入样例 1852.in

2
5 1000
01110
10111
00000
00000
00000
4 1
0111
0000
0000
0000

输出样例 1852.out

2 (第一组测试数据:结点0的starnumber是1, 结点1的star number是5,其他结点的starnumber都是0)
1 (第二组测试数据)

题目分析:这就是一个很裸的floyd。我们预处理出每一个节点的star number,然后记f[i][j][k]为i到j,而且中间经过的节点编号不大于k的路径的最多节点数,用f[i][k][k-1]+f[k][j][k-1]-1更新它即可(前提是要能够到达这两个状态)。

我们与处理所有的组合数C,注意这个值很大(50取25就已经炸long long了),又不能模。但我们发现G只有10^9,当C大于10^9时直接令它等于10^9+1即可。Star number也是同理。

注意输出答案时要分类讨论,如果最后f[i][i][N]可以到达,就说明有环,否则看一下f[i][j][N]的最大值。如果所有状态f[i][j][N]都不可到达,我们还要考虑路径上只有一个点的情况,实在不行才输出0。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=55;
const int oo=1000000001;

int C[maxn][maxn];

int deg[maxn];
int g[maxn];
int f[maxn][maxn][maxn];

bool e[maxn][maxn];
int ng,N,G;

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	
	for (int i=0; i<maxn; i++) C[i][0]=1;
	for (int i=1; i<maxn; i++)
		for (int j=1; j<=i; j++)
		{
			C[i][j]=C[i-1][j]+C[i-1][j-1];
			if (C[i][j]>oo) C[i][j]=oo;
		}
	scanf("%d",&ng);
	while (ng--)
	{
		memset(deg,0,sizeof(deg));
		scanf("%d%d",&N,&G);
		for (int i=1; i<=N; i++)
			for (int j=1; j<=N; j++)
			{
				char c=getchar();
				while ( c!='0' && c!='1' ) c=getchar();
				e[i][j]=(c-'0');
				if (e[i][j]) deg[i]++;
			}
		memset(g,0,sizeof(g));
		for (int i=1; i<=N; i++)
			for (int j=3; j<=deg[i]; j++)
			{
				g[i]+=C[ deg[i] ][j];
				if (g[i]>oo) g[i]=oo;
			}
		memset(f,-1,sizeof(f));
		for (int i=1; i<=N; i++)
			for (int j=1; j<=N; j++)
				if ( e[i][j] && 0<g[i] && g[i]<=g[j] && g[j]<=G ) f[i][j][0]=2;
		for (int k=1; k<=N; k++)
			for (int i=1; i<=N; i++)
				for (int j=1; j<=N; j++)
				{
					f[i][j][k]=f[i][j][k-1];
					if (f[i][k][k-1]==-1) continue;
					if (f[k][j][k-1]==-1) continue;
					f[i][j][k]=max(f[i][j][k],f[i][k][k-1]+f[k][j][k-1]-1);
				}
		bool sol=true;
		int ans=-1;
		for (int i=1; i<=N; i++)
			if (f[i][i][N]!=-1)
			{
				sol=false;
				printf("-1\n");
				break;
			}
		if (sol)
			for (int i=1; i<=N; i++)
				for (int j=1; j<=N; j++)
					ans=max(ans,f[i][j][N]);
		if (ans!=-1)
		{
			sol=false;
			printf("%d\n",ans);
		}
		if (sol)
			for (int i=1; i<=N; i++)
				if ( 0<g[i] && g[i]<=G )
				{
					sol=false;
					printf("1\n");
					break;
				}
		if (sol) printf("0\n");
	}
	
	return 0;
}

T2:

职员分配

题目描述

由于Blue Mary呕心沥血的管理,Blue Mary的网络公司蒸蒸日上。现在一共拥有了n名职员,可惜没有任何的金钱和声誉。平均每名每天职员都可以给公司带来x单位金钱或者y单位声誉(名利不能双全)。并且可以花费z单位的金钱在人才交易市场发布广告招聘职员,每次发布广告三天以后就会招聘到一名职员,并且必须在发布广告并且招聘到职员的那一天才能发布下一次广告。
Blue Mary计划以最快的时间获得至少A单位金钱和至少B单位声誉,请你计算一下他至少需要多少时间才能达到他的目标。

输入格式 1853.in

输入有且仅有一行,包含六个整数n,x,y,z,A和B,意义如题目描述所述。

输出格式 1853.out

要求输出一行,包含一个整数,表示Blue Mary至少需要多少时间才能达到他的目标。

输入样例 1853.in

输入样例一:
1 2 3 4 5 6
输入样例二:
3 2 3 2 19 18

输出样例 1853.out

5
6
约定:
1<=n,x,y,z,A,B<=20
说明:
规定每天先去人才市场,然后再去赚钱(或名誉)。

题目分析:(这题数据真小,感觉爆搜写得好都可以……)我一开始想了一个n^4的DP。我们一开始用一个bool数组f[d][a][b][num]记录能否到达(到第d天,拥有a的金钱,获得b的声誉,有num个员工)的状态。然后我们发现num肯定是越大越好,于是改成f[d][a][b]=num。枚举n^3,转移n。

后来经ke_kxy大牛指点才恍然大悟:声誉其实是没有用的!你要获得b的声誉,其实相当于你要获得round(b/y)*x的金钱(round表示向上取整)。于是状态就变成了f[d][a]=num,表示到第d天,获得a元钱所拥有的最大员工(或者其他状态表示也可以),然后就可以随便乱搞了。

一开始写代码的时候当a<z我也去买员工,结果写炸了一次……至于a的那一维要开多大,10000总可以了吧?

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=26;
const int maxm=10000;

int f[maxn][maxm];
int n,x,y,z,a,b;

bool Judge(int x)
{
	for (int i=a; i<maxm; i++)
		if (f[x][i]!=-1) return true;
	return false;
}

int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	
	scanf("%d%d%d%d%d%d",&n,&x,&y,&z,&a,&b);
	a+=((b+y-1)/y*x);
	memset(f,-1,sizeof(f));
	f[0][0]=n;
	int t=0;
	while ( !Judge(t) )
	{
		for (int i=0; i<a; i++)
			if (f[t][i]!=-1)
			{
				int &h=f[t+1][ i+f[t][i]*x ];
				h=max(h,f[t][i]);
				if (i>=z)
				{
					int &k=f[t+3][ i+f[t][i]*x*3-z ];
					k=max(k,f[t][i]+1);
				}
			}
		t++;
	}
	printf("%d\n",t);
	
	return 0;
}

T3:

加油站

题目描述

奶牛们开车到郊外旅游,由于驾驶技术不行,不小心撞到了石头,油箱漏油了。 现在汽车每走一单位距离,油箱就损耗和漏掉共一单位油。
为了修车,必须把车开到最近的小镇(不会超过10,000,000单位距离),到小镇的路可以认为是在一条直线上。从现在的位置到小镇途中, 共有 N (1 <= N<= 50,000) 个加油点,第i个加油点位于距离小镇有D_i (1 <= D_i < L)单位距离的地方,该加油站储油F_i (1<= F_i <= 100)单位.由于路途艰险,奶牛们想尽量把加油的次数减到最少.汽车的油箱可以认为是无穷大. 汽车现在为于距离小镇L个单位距离的地方,并且有P单位的油(1<= P <= 10,000,000).请你计算最少要加多少次油才能到达小镇,或者汽车根本开不到小镇. 注意:在同一地点,可能有多个加油站,但是,在计算时,它们是独立的,认为是两个不同的加油站。

输入格式 1854.in

* 第1行: 三个整数: N、 L、P.
* 第 2..N+1行: 两个整数: D_i 、 F_i. 对应着一个加油站。

输出格式 1854.out

* 一行: 到达小镇需要的最少的加油次数,如果无法到达小镇, 输出-1.

输入样例 1854.in

4 25 10
4 4
5 2
11 5
15 10

输出样例 1854.out

2
【输入解释】
汽车现在在距离小镇25个单位距离的地方,汽车有10个单位的汽油. 
在途中, 有 4 个加油站,他们分别位于距离小镇: 4、 5、 11、15个单位距离的地方。(可以看出,它们跟汽车现在位置的距离分别是:21、20、14、10) 。 这4个加油站的储油量分别是: 4、 2、 5、10。
【输出解释】
汽车开10单位距离后, 停车加10单位的油, 再开4单位距离,停车加5单位的油, 然后就可以开到小镇了。

题目分析:极其傻逼的贪心……。我们发现只要你经过了某一个加油站,接下来无论什么时候加油都一样。于是我们看一下当前的油能不能到下一个加油站(或终点),不能的话在之前那些加油站之中,选一个油最多的加。

发现n=50000,各路大牛都在用数据结构维护最大值。然而只有我这种蒟蒻发现f<=100,开了个100的数组……

一开始我没有判断能不能到终点,只是判断能不能到最后一个加油站,导致拿了一次60分。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=50010;
const int maxf=105;

struct data
{
	int d,f;
} sta[maxn];

int cnt[maxf];
int l,p;

int n,ans=0;

bool Comp(data x,data y)
{
	return x.d>y.d;
}

int Get()
{
	for (int i=100; i>=1; i--)
		if (cnt[i])
		{
			cnt[i]--;
			return i;
		}
	return 0;
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	
	scanf("%d%d%d",&n,&l,&p);
	for (int i=1; i<=n; i++) scanf("%d%d",&sta[i].d,&sta[i].f);
	sort(sta+1,sta+n+1,Comp);
	n++;
	sta[n].d=0;
	sta[n].f=0;
	int h=1;
	while (sta[h].d>l) h++;
	while (h<=n)
	{
		while (l-p>sta[h].d)
		{
			int x=Get();
			if (!x)
			{
				printf("-1\n");
				return 0;
			}
			p+=x;
			ans++;
		}
		p-=(l-sta[h].d);
		l=sta[h].d;
		cnt[ sta[h].f ]++;
		h++;
	}
	printf("%d\n",ans);
	
	return 0;
}

你可能感兴趣的:(dp,floyd)