【基礎練習】【單調隊列和其他】10.19.2015校內測試總結

雖然最後也沒有寫完而且不想寫了 但還是寫一下題解 會有用的

題目:http://www.doc88.com/p-1196564818693.html 大概是這樣


1.集卡片

用一個單調隊列(或者說不重複隊列)掃一遍即可。對於每個新掃到的元素,入隊前先檢查他是否與隊首的元素相同,如果相同,那麼由於我們要求種類齊全的情況下花費最少,隊首元素就是多餘的了,把隊首所有相同元素pop掉,然後新元素入隊。在這個過程中,ty變量統計隊列內部元素種類,同時用一個int數組記錄隊列內每種元素有多少個。

如果某時刻發現隊內元素種類恰好已包含所有種類,那麼這就可能是一個可行解。這時,由於要求連續的序列且花費最少,在隊首元素個數大於1時把隊首的元素都出隊,留下的就是儘量少但又種類齊全的“必需元素”了,此時檢查結果是否更優即可。

代碼:

//card 
//copyright by ametake
#include
#include
#include
#include
using namespace std;

const int maxn=1000000+10;
const int maxm=2000+10;
const int oo=0x3f3f3f;

int n,m,v;
int t[maxm];
int app[maxm];
int ty=0,price=0,ans=-oo;
queue q;

inline int read()
{
    int a=0;
    char ch=getchar();
    while (ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        a=a*10+ch-'0';
        ch=getchar();
    }
    return a;
}

int main()
{
    //freopen("1.txt","r",stdin);
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    n=read();
    m=read();
    v=read();
    for (int i=1;i<=m;i++)
    {
        t[i]=read();
    }
    int now;
    for (int i=1;i<=n;i++)
    {
        now=read();
        while (!q.empty()&&q.front()==now)
        {
            q.pop();
            price-=t[now];
            app[now]--;
            if (app[now]==0) ty--;
        }
        q.push(now);
        price+=t[now];
        if (!app[now])
        {
            ty++;
        }
        app[now]++;
        if (ty==m)
        {
            while (app[q.front()]>1)
            {
                app[q.front()]--;
                price-=t[q.front()];
                q.pop();
            }
            ans=max(v-price,ans);
        }
    }
    if (ans<0) printf("NO ans");
    else printf("%d",ans);
    return 0;
}
//15542 but 15396

2.天堂

由於每一層是各自獨立的,每一層單獨處理即可。但這道題目喪心病狂輸入數據非要讓你沒辦法在線處理,必須先讀入完數據。

對於每一層,f[i,j] 表示第i步,走到第j個點的最優情況。

方程顯然f[i,j]:=min{f[i-1,k]+dist(k,j)}

三重循環枚舉即可。注意體重小於浮力直接上浮;還有就是如果多個店在同一點上這一點可以拿任意多的氣球,不過這不影響DP

代碼有嚴重錯誤,還未找出,只過了五個點,在這裏就不放上了


3.環遊世界

這道題目我退了一上午自以爲正確但是隻過了五個點,而且周圍人還紛紛態度不太好,弄得我一下午心情很低落。我至今堅持認爲我的思想是正確的,雖然程序出了錯誤,但我想那是因爲DP初始狀態沒有考慮周到。

這道題其實我也是做麻煩了,很簡單的用數學做就可以了。考慮圖中只存在鏈和獨立的點。把每條連當做一個點,對所有點求一次全排列個數(即階乘)即可。而由於每條鏈有正反兩種接入方式,每條鏈都會使方案數乘二,因此對鏈的個數做一次快速冪 二的冪次方 再相乘即可


//travel
//copyright by ametake
#include
#include
#include
using namespace std;

const int maxn=50+10;
const int mo=1000000007;
int n;
int have[maxn][3];
bool visited[maxn];
long long lian,lidi,ans=0;
int until;
bool flag=false;

inline int read()
{
    int a=0;
    char ch=getchar();
    while (ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        a=a*10+ch-'0';
        ch=getchar();
    }
    return a;
}

void dfs(int x)//¿´¿´Ê²Ã´Ê±ºò¼ÓÉÏlian 
{/*
    if (visited[x])
    {
        flag=true;
        return;
    }*/
    visited[x]=true;
    lidi++;
    until++;
    if (have[x][0]==1&&until==1) dfs(have[x][1]);
    else if (have[x][0]==2)
    {
        if (visited[have[x][1]]&&visited[have[x][2]])
        {
            flag=true;
            return;
        }
        else if (visited[have[x][1]]) dfs(have[x][2]);
        else dfs(have[x][1]);
    }
    else if (until>1) lian++;
}


long long mi(long long a,long long b,long long c){
	long long ans=1;long long x,y,z;x=a;y=b;z=c;
	while (b>0){
		if (b & 1==1) ans=(ans*a)%c;
		a=(a*a)%c;
		b>>=1;
	}
	return ans;
}

long long mul(long long k)
{
    long long now=1;
    for (int i=2;i<=k;i++)
    {
        now=(now*i)%mo;
    }
    return now;
}

int main()
{
    //freopen("1.txt","r",stdin);
    //freopen("2.txt","w",stdout);
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    n=read();
    char c[maxn];
    for (int i=1;i<=n;i++)
    {
        scanf("%s",c);
        for (int j=0;j2)
                {
                    printf("0");
                    return 0;
                }
                have[i][have[i][0]]=j+1;
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        if (have[i][0]==1&&(!visited[i]))
        {
            until=0;
            dfs(i);
        }
        if (flag)
        {
            printf("0");
            return 0;
        } 
    }
    for (int i=1;i<=n;i++) if (!visited[i]&&have[i][0]>=1)
    {
        printf("0");
        return 0;
    }
    n=n-lidi+lian;
    ans+=mi(2,lian,mo);
    ans*=mul(n);
    printf("%lld",ans%mo);
    return 0;
}


4.H1N1

這道題目我並沒有仔細看···但據說是和很久以前的一道取水的圖論相似···

然而聽了里奧神犇講解還是不會= =於是奧神發來了題解,於是我可恥地把題解粘到這兒來了

看口氣應該是奧神自己寫的···版權應該是奧神所有,再次表示感謝:

要達到一眼出解的熟練度。
先縮小問題規模,比如不考慮可以建設防疫站的城市。
如果只有一個有防疫站的城市,任務就是把所有點用最小的代價連通。說白了,求最小生成樹。
然而題目中有防疫站的城市太多(超過了一個),所以想辦法把防疫站縮爲一個。我們可以設一個超級城市,讓它有防疫站,讓所有有防疫站的城市和它都有權值爲0的無向邊,那麼原來有防疫站的城市本質上都變成了這一個城市。
其餘城市按照題目給出的鄰接矩陣連邊,如果有公路則邊權爲零,否則爲建造公路的代價。那如果沒有可建造防疫站的城市,答案就是改造過的新圖的最小生成樹總權值。
現在考慮可以建防疫站的城市,初看似乎有困難……
超級城市有防疫站,又和所有有防疫站的城市有着權值爲零的邊,所以可以讓原來有防疫站的城市沒有防疫站。。。
我想說什麼?
其實,其實就是如果想讓某一個城市有防疫站,就可以讓這個城市向超級城市連一條邊,權值是讓這個城市有防疫站的代價。一開始有防疫站的城市不需要任何代價就可以有防疫站,所以它們向超級城市連邊的邊權是零;而可以建造防疫站的城市有防疫站的代價就是在該城市建造防疫站的花費,所以只要以此花費爲邊權從該城市向超級城市連邊,就處理好了這一類城市。
答案就是按上述方法構成的圖的最小生成樹的權值。



顯然我沒有代碼···


所以,其實這幾道題目看了題解都挺簡單的,但是就是想不到。還是應該更多的鍛鍊自己的思維能力,形成一眼就知道模型的熟練度。還是要多看、多想、多練。


NOIP2015 加油

明天就是重陽節了。玉枕紗櫥,半夜涼初透。

——天與秋光,轉轉情傷,探金英知近重陽

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