[SinGuLaRiTy] 2017-07-26 綜合性測試

【SinGuLaRiTy-1032】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.ios

             

單詞 (word)

題目描述

有一個有字母表,一共有N行M列,你從左上角開始出發,目的地是右下角。每次你只能往右或往下走一步。將你通過的格子裏面的字母按照訪問順序組成一個單詞。求你能獲得的字典序最小的單詞是什麼?數據結構

輸入

第一行包含N和M,(1<=N,M<=2000)
接下來N行,每行包含M個小寫字母。優化

輸出

輸出最小字典序的單詞。
40%的數據,每一個格子的右、下的字母不一樣。ui

樣例數據

樣例輸入1 樣例輸出1

4 5
ponoc
ohoho
hlepo
mirkospa

pohlepko3d

樣例輸入2 樣例輸出2

4 5
bbbbb
bbbbb
bbabb
bbbbbcode

bbbbabbb
樣例輸入3 樣例輸出3

2 5
qwert
yuiopblog

qweiopget

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<樣例解釋>string

對於樣例一,下面表示了一種字典序最小的解法:

解析

若是往右或往下的不同,那麼沒的說,直接往字典序小的走就好了。
不過,噹噹前位置上,往左或往下的同樣的時候,問題就來了——咱們在當前是不能肯定往右仍是往左的。對此,每走一步,咱們能夠開一個列表,來保存在必定步數後咱們能到達的全部的最佳位置。咱們從一個包含初始字符(0,0)的列表開始,在接下來的每一步中更新列表,便於咱們查詢當前列表中全部位置的鄰位的最小字典序的值,而後建立一個全部具備這個最小值的鄰位的位置。由於咱們能夠經過兩種方式到達一個位置,咱們須要注意到不要將同一個位置重複放入列表中,不然咱們將在每一次迭代中複製相同位置的出現次數。

Code

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<algorithm>
#include<cstring>

using namespace std;

typedef pair<int, int> point;

#define x first
#define y second

const int MAX=2010;

int n,m;
char grid[MAX][MAX];
bool vis[MAX][MAX];

int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
        scanf("%s",grid[i]);

    vector<point> curr,next;
    for(curr.push_back({0,0});!curr.empty();curr=next)
    {
        point p=curr.back();
        printf("%c",&grid[p.x][p.y]);
        char mn='z';
        for(point pt : curr)
        {
            int dx=1,dy=0;
            for(int i=0;i<2;i++)
            {
                swap(dx,dy);
                int nx=pt.x+dx;
                int ny=pt.y+dy;
                if(nx>=n||ny>=m)
                    continue;
                mn=min(mn,grid[nx][ny]);
            }
        }
        next.clear();
        for(point pt : curr)
        {
            int dx=1,dy=0;
            for(int i=0;i<2;i++)
            {
                swap(dx,dy);
                int nx=pt.x+dx;
                int ny=pt.y+dy;
                if(nx>=n||ny>=m)
                    continue;
                if(vis[nx][ny])
                    continue;
                if(grid[nx][ny]==mn)
                {
                    next.push_back({nx,ny});
                    vis[nx][ny]=1;
                }
            }
        }
    }
    return 0;
}

玻璃杯 (drink)

題目描述

你有N個容量無限大的玻璃杯,每一個玻璃杯都有一些水。你想要喝光全部的水,可是你最多隻能喝k個玻璃杯。怎麼辦呢?你能夠把一個玻璃杯的水所有倒入另外一個玻璃杯,。可是你將第i個玻璃杯中的水倒入第j個玻璃杯,須要花費代價Cij。如何花費最少的代價,讓你能喝光全部的水。

輸入

第一行包含整數N,K(1<=K<=N<=20);
接下來N行,每行包含N個整數Cij(0<=Cij<=10^5)。第i行第j列的數表示Cij。Cii必定是0.

輸出

輸出最小的代價。
40%的數據,N<=10。

樣例數據

樣例輸入1 樣例輸出1

3 3
0 1 1
1 0 1
1 1 0

 0
樣例輸入2 樣例輸出2

3 2
0 1 1
1 0 1
1 1 0

 1
樣例輸入3 樣例輸出3

5 2
0 5 4 3 2
7 0 4 4 4
3 3 0 1 2
4 3 1 0 5
4 5 5 5 0

 5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

解析

這是一道DP題。咱們能夠用一個20位的二進制數來表示當前的玻璃杯的狀態。咱們能夠作的,就是將一個玻璃杯裏的水倒出到另外一個杯子裏,狀態轉移的時間複雜度爲O(N^2),不過經過優化彷佛能夠跑的更快。總時間複雜度就是O(2*N*N^2).

Code

#include<cstdio>
#include<string>
#include<vector>
#include<map>
#include<cstdlib>
#include<algorithm>
#include<cstring>

using namespace std;

typedef pair <int, int> pii;

const int MAXN = 21;

int n, k;
int a[MAXN][MAXN];
int dp[1<<MAXN];

int solve(int mask)
{
    if(__builtin_popcount(mask)==k)
        return 0;
    int &ret=dp[mask];
    if(ret!=-1)
        return ret;
    ret=1<<30;
    for(int i=0;i<n;++i)
    {
        if(!(mask&(1<<i)))
            continue;
        for(int j=0;j<n;++j)
        {
            if(i==j)
                continue;
            if(!(mask&(1<<j)))
                continue;
            ret=min(ret,solve(mask^(1<<i))+a[i][j]);
        }
    }
  return ret;
}

int main()
{
    memset(dp,-1,sizeof dp);
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            scanf("%d",&a[i][j]);
    printf("%d",solve((1<<n)-1));
    return 0;
}

數列 (sequence)

題目描述

你有N個正整數,開始時,你在黑板上寫下第一個數,寫第二個數時,你能夠在第一個數的左邊寫,也能夠在第一個數的右邊寫。你每寫一個數,均可以選擇在前面已經寫過的數的左邊或者右邊的位置。不一樣的寫法能夠獲得不一樣的數列。在全部可能獲得的數列中找出最長的上升子序列,求出它的長度。設它的長度爲M,你還需求出長度爲M的上升子序列有多少種?注意:在不一樣的數列中的最長上升子序列都是不一樣的;即便在同一個數列中的兩個最長上升子序列,只要有一個數的位置不同,也算不一樣的子序列。

輸入

第一行一個整數N(1<=N<=2*10^5)
接下來N個空格隔開的整數,表示你擁有的N個數。每個數均不超過10^9。

輸出

只有一行,包含兩個數,第一個數爲最長的上升子序列的長度。

樣例數據

樣例輸入1 樣例輸出1

2
1 1

1 4
樣例輸入2 樣例輸出2

4
2 1 3 4

4 1

 

 

 

 

 

 

 

 

<樣例解釋>

樣例數據一:

最長上升子序列長度爲1.
首先有兩種寫的順序,第一種是第二個數在第一個數的左邊,第二種是第二個數在第一個數的右邊。在每一種中,你能夠選擇第一個數做爲最長上升子序列,也能夠選擇第二個數做爲最長上升子序列。因此一共有4種。

樣例數據二:

你有八種寫的順序。可是隻有一種順序可以獲得最長上升子序列,即1 2 3 4,其中的最長上升子序列是惟一的。因此答案是4 1.

<數據範圍>

30%的數據,N<=20
50%的數據,N<=1000

解析

爲了肯定最長上升子序列的長度,對於初始序列中的每個位置X,必須肯定從X的右側開始並終止於X的最長上升子序列的長度(序列從右到左讀取),以及咱們達到這個狀態的方法的數量。這個思路也一樣適用於最長降低子序列。咱們能夠經過相對簡單的方法來實現,即便用Fenwick Tree的數據結構,時間複雜度爲O(N*logN)。
咱們會注意到,解決方案是嚴格上升子序列和嚴格降低子序列的並集,其中上升子序列的最大元素小於降低子序列的最小元素。若是A是在位置X(包含X)結束的嚴格上升子序列的長度,B也是在位置X(包含X)結束的嚴格降低子序列的長度,num_A,num_B分別是獲得它們的方法數,那麼X(包含X)右側的數字的最大長度就是A+B-1,獲得這個解的方法數爲num_A*num_B。
所要求的最大長度就是每個位置對應的最大長度的最大值。咱們用R來表示這個最大值。咱們能達到這個最大長度的方法數,就是對應的最大長度等於R*(2N-R)的當前位置的方案數的乘積。
其中的2N-R是必要的,由於若是一個解包含R個數,那麼剩下的N-R個數當中的每個均可以被獨立地放置在以前的全部數的前面或後面。
解法的總時間複雜度爲O(N*logN)。

Code

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

typedef long long int ll;
typedef pair<int, int> par;

#define X first
#define Y second

const int MAXN=500010,MOD=1000000007;

inline int add(int a,int b)
{
    int ret=a+b;
    if(ret>=MOD)
        ret-=MOD;
    return ret;
}

inline int mul(int a,int b)
{
    ll ret=(ll)a*b;
    if(ret>=MOD)
        ret%=MOD;
    return ret;
}

int n;
int niz[MAXN],dva[MAXN];

par A[MAXN],B[MAXN];
par FWT_up[MAXN],FWT_down[MAXN];

par rj;

par point(par a,par b)
{
    if(b.X>a.X)
    {
        a.X=b.X;
        a.Y=b.Y;
    }
    else if(b.X==a.X)
        a.Y=add(a.Y,b.Y);
    return a;
}

void put_up(int x,par v)
{
    x+=5;
    for(;x<MAXN;x+=x&-x)
        FWT_up[x]=point(FWT_up[x],v);
}

par query_up(int x)
{
    x+=5;
    par ret(0,0);
    for(;x>0;x-=x&-x)
        ret=point(ret,FWT_up[x]);
    return ret;
}

void put_down(int x,par v)
{
    x+=5;
    for(;x>0;x-=x&-x)
        FWT_down[x]=point(FWT_down[x],v);
}

par query_down(int x)
{
    x+=5;
    par ret(0,0);
    for(;x<MAXN;x+=x&-x)
        ret=point(ret,FWT_down[x]);
    return ret;
}

void work()
{
    vector<int> v;
    for(int i=0;i<n;i++)
        v.push_back(niz[i]);
    sort(v.begin(),v.end());
    v.resize(unique(v.begin(), v.end())-v.begin());
    for(int i=0;i<n;i++)
        niz[i]=lower_bound(v.begin(),v.end(),niz[i])-v.begin();
}

void mid_up()
{
    for(int i=n-1;i>=0;i--)
    {
        par p=query_up(niz[i]-1);
        if(p.X==0)
        {
            A[i]=par(0,1);
            put_up(niz[i],par(1,1));
        }
        else
        {
            A[i]=p;
            p.X++;
            put_up(niz[i],p);
        }
    }
}

void mid_down()
{
    for(int i=n-1;i>=0;i--)
    {
        par p=query_down(niz[i]+1);
        if(p.X==0)
        {
            B[i]=par(0,1);
            put_down(niz[i],par(1,1));
        }
        else
        {
            B[i]=p;
            p.X++;
            put_down(niz[i],p);
        }
    }
}

void fang()
{
    dva[0]=1;
    for(int i=1;i<MAXN;i++)
        dva[i]=mul(dva[i-1],2);
}

void Main()
{
    for(int i=0;i<n;i++)
        rj=point(rj,par(A[i].X+1+B[i].X,mul(A[i].Y,B[i].Y)));
    rj.Y=mul(rj.Y,dva[n-rj.X]);
}

int main()
{
    fang();
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d", &niz[i]);
    work();
    mid_up();
    mid_down();
    Main();
    printf("%d %d\n",rj.X,rj.Y);
    return 0;
}

 

Time: 2017-07-26

相關文章
相關標籤/搜索