2018_9_8 模拟题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/82530020

前言:比较OTL


JZOJ 3470 最短路

题目

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


分析

k s p f a ,在起点跑一次 s p f a ,然后剩下的其实就是深搜或者状压了(我用了状压),时间复杂度 O ( k × × E + 2 11 × 11 2 )


代码

#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 )


代码

#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 个格点,问能组成多少个三角形


分析

O ( n 3 ) 伪代码

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 ) ,也就是枚举一个点,然后求出每个点到该点的斜率,再判断即可


代码

#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;
}

后续:我要报提高组!

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/82530020