暑假集训test7

所以我又回来了,这次是test7。
(test1,test2,test3,集体失踪,敬请期待(摊手))
嗯,喜大普奔。
上题。

1.1807

题目描述

给出一个由数字(‘0’-‘9’)构成的字符串。我们说一个子序列是好的,如果他的每一位都是 1、8、0、7 ,并且这四个数字按照这种顺序出现,且每个数字都出现至少一次(111888888880000007 是好的,而 1087 不是)。请求出最大的好的子序列的长度。

输入格式

输入唯一一行一个字符串。

输出格式

一行一个整数表示答案。

样例数据

输入
1800777700088888000777
输出
13

备注

【数据范围】
对 30% 的输入数据 :字符串长度≤100 ;
对 100% 的输入数据 :字符串长度≤1000000 。

第一次看到题的时候纠结了好久,感觉怎么做都考虑不完全。
最后决定把‘1’赋为1,‘8’赋为2,‘0’赋为3,‘7’赋为4。
然后用了最长不下降子序列。
但是bug简直太多了。
首先不仅2大于1,3也大于1,4也是。
然后又不能保证1、2、3、4四个数字都会出现。
于是又在不同情况下做了分类讨论,讨论出来3kb。
然后……然后只有10分。
这是一个黄少天听了沉默,韩文清看了流泪的故事。
好了,代码和正解。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int len,ans;
int a[1000005],f[1000005][5];
char s[1000005];

int main()
{
    //freopen("1807.in","r",stdin);
    //freopen("1807.out","w",stdout);
    memset(f,128,sizeof(f));

    scanf("%s",s+1);
    len=strlen(s+1);
    f[0][0]=0;
    for(int i=1;i<=len;i++)
    {
        f[i][0]=f[i-1][0];
        f[i][1]=f[i-1][1];
        f[i][2]=f[i-1][2];
        f[i][3]=f[i-1][3];
        f[i][4]=f[i-1][4];
        if(s[i]=='1')
            f[i][1]=max(f[i][1],f[i-1][0])+1;
        if(s[i]=='8')
            f[i][2]=max(f[i][2],f[i-1][1])+1;
        if(s[i]=='0')
            f[i][3]=max(f[i][3],f[i-1][2])+1;
        if(s[i]=='7')
            f[i][4]=max(f[i][4],f[i-1][3])+1;
    }
    cout<<max(0,f[len][4])<<endl;
    return 0;
}

开一个二维数组(其实一维也可以,为了叙述方便)f[i][j],i表示现在已经取了前面i位,用到了第j个数字(即1、8、0、7),最后输出f[n][4]。
至于动态转移方程,e.g.对于‘8’来说,只有前面是‘1’或是‘8’时才有更新的资格,否则没有。

2.Minimum

题目描述

给出一幅由 n 个点 m 条边构成的无向带权图。
其中有些点是黑点,另外点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到黑点的距离任然是最短距离。

输入格式

第一行两个整数 n,m ;
第二行 n 个整数,0 表示白点,1 表示黑点;
接下来 m 行,每行三个整数 x,y,z ,表示一条连接 x 和 y 点,权值为 z 的边。

输出格式

如果无解,输出“impossible”,否则,输出最小代价。

样例数据

输入
5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5

输出
5

备注

【样例说明】
选 2、4、6 三条边。

【数据范围】
对 30% 的输入数据 :1≤n≤10,1≤m≤20;
对 100% 的输入数据 :1≤n≤100000,1≤m≤200000,1≤z≤1000000000 。

嗯很好,其实第一遍看的时候根本没有理解题目的意思。
好吧最后还是懂了,就是找一个白点到周围黑点的最短路,若有多个全部相加。(然而懂了也并没有什么用)【滑稽】
直接说说考完后拿到的正解吧。

整个解题过程分为两块,但前提是还要证明两条规律。
1.最短路上的所有点必连通,且一条最短路上只有最末点是黑点,其余皆为白点。
2.每个点连在最短路上都只连一次,即一个白点到黑点的最短路只有一条。
所以整个问题就分为先用各种算法(喜欢就好)求最短路,然后再建最小生成树,注意能连通的尽量都要连通。

具体做法可以取一个超级点S,在S与每个黑点之间连权值为0的边(可以理解为把所有的黑点都变成起点),先处理从S出发到每个点的最短距离,获得最短路径图一张。这样以后我们需要取权值最小的边的集合使这幅图连通,此时用到Kruskal算法求最小生成树。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define S 0
using namespace std;

struct node
{
    int next;
    int to;
    int w;
}bian[3000003];
struct gl
{
    int x;
    int y;
    int w;
}e[3000003];
int n,m,tot,cnt,inf,cont;
long long ans,dis[100010];
int first[100010],b[3000003],c[100010];
bool exist[100010];

inline void create(int x,int y,int z)
{
    tot++;
    bian[tot].next=first[x];
    first[x]=tot;
    bian[tot].to=y;
    bian[tot].w=z;
}

inline void czh(int x,int y,int z)
{
    e[++cnt].x=x;
    e[cnt].y=y;
    e[cnt].w=z;
}

inline void zql(int s)
{
    memset(dis,40,sizeof(dis));
    inf=dis[0];
    int head=0,tail=1;
    dis[s]=0;
    b[1]=s;
    exist[s]=true;
    while(head^tail)
    {
        if(++head==3000000)
            head=1;
        int now=b[head];
        exist[now]=false;
        for(int i=first[now];i;i=bian[i].next)
        {
            int next=bian[i].to;
            if(dis[next]>dis[now]+bian[i].w)
            {
                dis[next]=dis[now]+bian[i].w;
                if(!exist[next])
                {
                    exist[next]=true;
                    if(++tail==3000000)
                        tail=1;
                    b[tail]=next;
                }
            }
        }
    }
}

inline int find(int x) 
{ 
    return x==c[x]?x:c[x]=find(c[x]); 
} 

inline bool comp(gl a,gl b) 
{ 
    return a.w<b.w; 
} 

int main()
{
    //freopen("minimum.in","r",stdin);
    //freopen("minimum.out","w",stdout);

    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int u;
        cin>>u;
        if(u==1)
            create(S,i,0);
    }   
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        create(x,y,z);
        create(y,x,z);
    }
    zql(S); 
    for(int i=1;i<=n;i++) 
        if(dis[i]==inf) 
        { 
            cout<<"impossible"<<endl; 
            return 0; 
        }   

    for(int i=1;i<=n;i++) 
        for(int j=first[i];j;j=bian[j].next) 
        { 
            if(dis[bian[j].to]+bian[j].w==dis[i]) 
                czh(bian[j].to,i,bian[j].w); 
        } 
    sort(e+1,e+1+cnt,comp); 
    for(int i=1;i<=n;i++) 
        c[i]=i; 
    for(int i=1;i<=cnt;i++) 
    { 
        int u=e[i].x,v=e[i].y,w=e[i].w; 
        int pu=find(u),pv=find(v); 
        if(pu!=pv) 
        { 
            c[pu]=pv; 
            ans+=w; 
            if(++cont==n-1) 
                break; 
        } 
    } 
    if(ans) 
        cout<<ans<<endl;
    else 
        cout<<"impossible"<<endl; 
    return 0;       
}

3.(假装这里有题的样子)

好吧其实是真有的,但是莫比乌斯函数什么的数论那一坨还没有过多涉猎,基本上看着题解也是一脸大写的懵逼,所以就装作什么都不知道的样子,假装只有两道题,嗯咳。。。。。

来自2017.7.17
——我认为return 0,是一个时代的终结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值