暑假集訓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,是一個時代的終結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章