2018_9_8 模拟题

111 篇文章 0 订阅
76 篇文章 0 订阅

前言:比较OTL


JZOJ 3470 最短路

题目

给出一个 n n n个节点, m m m条边的有向图,并且有 k k k个点必须走过,问从起点 s s s到终点 t t t的最短路径


分析

k k k s p f a spfa spfa,在起点跑一次 s p f a spfa spfa,然后剩下的其实就是深搜或者状压了(我用了状压),时间复杂度 O ( k × 常 数 × E + 2 11 × 1 1 2 ) O(k\times 常数\times E+2^{11}\times 11^2) O(k××E+211×112)


代码

#include <cstdio>
#include <cstring>
#include <queue>
#define p 707406378
#define mmm memset(d,127/3,sizeof(d)),memset(v,0,sizeof(v))
typedef unsigned int uit;
struct node{uit y,w,next;}e[100001]; bool v[50001];
uit ls[50001],o[11],d[50001],dis[12][11],f[11][2048];
std::queue<uit>q; uit n,m,k,s,t,ans=4e9;
uit in(){
	uit ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
void spfa(){//spfa
	while (q.size()){
		uit x=q.front(); q.pop();
		for (register uit i=ls[x];i;i=e[i].next)
		if (d[x]+e[i].w<d[e[i].y]){
			d[e[i].y]=d[x]+e[i].w;
			if (!v[e[i].y])
			    v[e[i].y]=1,q.push(e[i].y);
		}
		v[x]=0;
	}
}
void hpfa(uit h){//预处理
	d[o[h]]=0; q.push(o[h]); v[o[h]]=1; spfa();
	for (register uit i=1;i<=k;i++) dis[i][h]=d[o[i]]; dis[11][h]=d[t];//终点标记
}
uit min(uit a,uit b){return a<b?a:b;}
void print(uit ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
int main(){
	n=in(); m=in(); k=in(); s=in(); t=in();
	for (register uit i=1;i<=m;i++){
		register uit x=in();
		e[i]=(node){in(),in(),ls[x]}; ls[x]=i;
	}
	for (register uit i=1;i<=k;i++) o[i]=in();
	for (register uit i=1;i<=k;i++) mmm,hpfa(i);//k个标记点spfa
	mmm,v[s]=1,d[s]=0,q.push(s),spfa();//从起点跑spfa
	if (!k) if (d[t]==p) putchar('-'),putchar(49); 
	            else print(d[t]);//没有标记点
	else{
		for (register uit i=1;i<(1<<k);i++)
		for (register uit j=1;j<=k;j++){
			f[j][i]=4e9;
			if (i&(1<<j-1)){//存在相同的点
				if (i^(1<<j-1)){//也存在不同的点
				for (register uit q=1;q<=k;q++)
				if (q!=j&&i&(1<<q-1))//找到中间点
				f[j][i]=min(f[j][i],f[q][i^(1<<j-1)]+dis[j][q]);//dp
				}else f[j][i]=d[o[j]];//那么直接赋为从起点到该标记点的最短路径
			}
		}
		uit ans=4e9;
		for (register uit i=1;i<=k;i++) ans=min(ans,f[i][(1<<k)-1]+dis[11][i]);//求最终答案(每个标记点到终点的距离+dp的结果)
		if (ans==4e9) putchar('-'),putchar(49); else print(ans);//可能没有解
	}
	return 0;
}

JZOJ 3487 剑与魔法

题目

有一些中止条件,在中止条件之前的任务数量必须小于中止条件的限制,问最多能获得多少任务点数


分析

用贪心的思想,要大的总比要小的好,于是建小根堆,每次插入任务点数,当满足中止条件不断弹出小根堆的堆顶,最后求出小根堆任务点数总和即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)


代码

#include <cstdio>
#include <queue>
std::priority_queue<int>q;
int n,ans;
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	n=in();
	for (register int i=1;i<=n;i++){
		char c=getchar();
		while (c<97||c>122) c=getchar();
		int x=in();
		if (c=='c') q.push(-x);//优先队列默认大根堆
		else if (i<n) while (q.size()>=x) q.pop();//弹出
	}
	while (q.size()) ans-=q.top(),q.pop();//求出答案
	return !printf("%d",ans);
}

JZOJ 3493 三角形

题目

在一个平面直角坐标系,有 n n n个格点,问能组成多少个三角形


分析

O ( n 3 ) O(n^3) O(n3)伪代码

ans=n*(n-1)*(n-2)/6(组合C(n,3)//任取三个点的方案
for (register int i=1;i<n-1;i++)
for (register int j=i+1;j<n;j++)
for (register int k=j+1;k<=n;k++)
if ((x[i]-x[j])*(y[i]-y[k])==(x[i]-x[k])*(y[i]-y[j])) ans--;//移项

然后就可以拿到60分
当然正解是 O ( n 2 l o g n ) O(n^2logn) O(n2logn),也就是枚举一个点,然后求出每个点到该点的斜率,再判断即可


代码

#include <cstdio>
#include <cmath>
#include <algorithm>
int n,x[3001],y[3001]; long long ans; double c[3001];
int in(){
	int ans=0,f=1; char c=getchar();
	while ((c<48||c>57)&&c!='-') c=getchar();
	if (c=='-') c=getchar(),f=-f;
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans*f;
}
int main(){
	n=in(); ans=(long long)n*(n-1)*(n-2)/6;//总方案
	for (register int i=1;i<=n;i++) x[i]=in(),y[i]=in();
	for (register int i=1;i<n-1;i++){
		int t=0,m=0;
		for (register int j=i+1;j<=n;j++)
		    if (x[i]==x[j]) t++;//特判
		    else c[++m]=(double)(y[j]-y[i])/(x[j]-x[i]);//斜率
		ans-=(long long)t*(t-1)>>1; //横着的特判
		std::sort(c+1,c+1+m); t=0;//排序
		for (register int j=1;j<=m;j++){
			if (j>1&&c[j]==c[j-1]) t++; else t=1;
			ans-=t-1;//减去三点在一直线的方案
		}
	}
	printf("%lld",ans);
	return 0;
}

后续:我要报提高组!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值