8.6模拟赛

T1.最短路path

求经过k个必经点的最短路

100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。

写了naive的算法,类似分层图的状压最短路,然而加成了双向边,炸成10分,感谢良心出题人。

正解考虑k+1个点到终点的最短路,+的1是起点,然后它们之间用状压DP求出彼此最短路,再加上到终点的最短路即为答案。

答案会超过MAX_INT

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define int long long
using namespace std;

inline int rd(){
    int ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

int n,m,num,s,t;
int lim[16];

const int MAXN=50005;

struct Edge{
    int next,to,w;
}e[MAXN<<1];
int ecnt,head[MAXN];
inline void add(int x,int y,int w){
    e[++ecnt].next = head[x];
    e[ecnt].to = y;
    e[ecnt].w = w;
    head[x] = ecnt;
}
struct Node{
    int id,w;
    Node(int x=0,int y=0){id=x;w=y;}
    bool operator<(const Node &rhs)const {
        return w>rhs.w;
    }
}top;
bool vis[MAXN];
int dis[MAXN][12];
priority_queue<Node> Q;
void dij(int st,int cur){
    memset(vis,0,sizeof(vis));
    while(!Q.empty()) Q.pop();
    dis[st][cur]=0;Q.push(Node(st,0));
    while(!Q.empty()){
        top=Q.top();
        Q.pop();
        int mn=top.w,mnid=top.id;
        if(dis[mnid][cur]!=mn) continue;
        vis[mnid]=1;
        for(int j=head[mnid];j;j=e[j].next){
            int v=e[j].to;
            if(mn+e[j].w<dis[v][cur]){
                dis[v][cur]=mn+e[j].w;
                Q.push(Node(v,dis[v][cur]));
            }
        }
    }
}
            
int f[1<<12][12];
signed main(){
    n=rd();m=rd();num=rd();s=rd();t=rd();
    int x,y,w;
    for(int i=1;i<=m;i++){
        x=rd();y=rd();w=rd();
        add(x,y,w);
    }
    for(int i=1;i<=num;i++){
        lim[i]=rd();
    }
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=num;i++){
        dij(lim[i],i);
    }
    lim[num+1]=s;
    dij(s,num+1);
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=num+1;i++) f[1<<(i-1)][i]=dis[lim[i]][num+1];
    for(int i=0;i<(1<<(num+1));i++){
        for(int j=1;j<=num+1;j++){
            if(!(i&(1<<(j-1)))) continue;
            for(int k=1;k<=num+1;k++){
                if(i&(1<<(k-1))) continue;
                f[i|(1<<(k-1))][k]=min(f[i|(1<<(k-1))][k],f[i][j]+dis[lim[k]][j]);
            }
        }
    }
    int ans=1ll<<60;
    for(int i=1;i<=num+1;i++){
        ans=min(ans,f[(1<<(num+1))-1][i]+dis[t][i]);
    }
    printf("%lld",ans==1ll<<60?-1:ans);
    return 0;
}
T1

T2.剑与魔法(dragons)

一些前缀限制(前缀不能选超过xi个数),最后一个不限制,还是hsz最巨。

您最强。

这是一个贪心,写了一个O(n^2)DP,还没考虑最后一个不限制..

用小根堆维护选出的数,遇到限制就不断pop直到符合限制,可以证明,最后剩下的就是最优解了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

inline int rd(){
    int ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

priority_queue<int> Q;
int n;

int main(){
    n=rd();
    char s[50];
    for(int i=1;i<=n-1;i++){
        scanf("%s",s);
        if(s[0]=='c') Q.push(-rd());
        else{
            int lim=rd();
            while(Q.size()>=lim) Q.pop();
        }
    }
    int ans=0;
    while(!Q.empty()) ans-=Q.top(),Q.pop();
    cout<<ans;
    return 0;
}
T2

猜你喜欢

转载自www.cnblogs.com/ghostcai/p/9433609.html
8.6