noip 2010總結

機器翻譯

題目背景

小晨的電腦上安裝了一個機器翻譯軟件,他經常用這個軟件來翻譯英語文章。

題目描述

這個翻譯軟件的原理很簡單,它只是從頭到尾,依次將每個英文單詞用對應的中文含義來替換。對于每個英文單詞,軟件會先在內存中查找這個單詞的中文含義,如果內存中有,軟件就會用它進行翻譯;如果內存中沒有,軟件就會在外存中的詞典內查找,查出單詞的中文含義然后翻譯,并將這個單詞和譯義放入內存,以備后續的查找和翻譯。

假設內存中有M個單元,每單元能存放一個單詞和譯義。每當軟件將一個新單詞存入內存前,如果當前內存中已存入的單詞數不超過M-1,軟件會將新單詞存入一個未使用的內存單元;若內存中已存入M個單詞,軟件會清空最早進入內存的那個單詞,騰出單元來,存放新單詞。

假設一篇英語文章的長度為N個單詞。給定這篇待譯文章,翻譯軟件需要去外存查找多少次詞典?假設在翻譯開始前,內存中沒有任何單詞。

輸入輸出格式

輸入格式:
輸入文件共2行。每行中兩個數之間用一個空格隔開。

第一行為兩個正整數M和N,代表內存容量和文章的長度。

第二行為N個非負整數,按照文章的順序,每個數(大小不超過1000)代表一個英文單詞。文章中兩個單詞是同一個單詞,當且僅當它們對應的非負整數相同。

輸出格式:
包含一個整數,為軟件需要查詞典的次數。

輸入輸出樣例

輸入樣例#1:

3 7
1 2 1 5 4 4 1

輸出樣例#1:

5

說明

每個測試點1s

對于10%的數據有M=1,N≤5。

對于100%的數據有0<=M<=100,0<=N<=1000。
整個查字典過程如下:每行表示一個單詞的翻譯,冒號前為本次翻譯后的內存狀況:

空:內存初始狀態為空。
1. 1:查找單詞1并調入內存。
2. 1 2:查找單詞2并調入內存。
3. 1 2:在內存中找到單詞1。
4. 1 2 5:查找單詞5并調入內存。
5. 2 5 4:查找單詞4并調入內存替代單詞1。
6. 2 5 4:在內存中找到單詞4。
7. 5 4 1:查找單詞1并調入內存替代單詞2。
共計查了5次詞典。

思路

模擬+隊列

如果一個數在隊列中,不用管
如果不在隊列中,判斷隊列大小:

  1. 如果size<=m-1,將這個數加入隊列
  2. 如果size>m,刪除隊首元素,將這個數壓入隊尾

數組模擬隊列即可

代碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, ans = 0, sizee = 0, b[10010];
int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; i++)
    {
        bool flag = false;
        int a;
        scanf("%d", &a);
        for (int j = 1; j <= sizee; j++)
            if (b[j] == a){
                flag = true;
                break;
            }
        if (flag == 0){
            ans++;
            if (sizee < m){
                sizee++;
                b[sizee] = a;
            }
            if (sizee == m){
                for (int j = 2; j <= m; j++)
                    b[j - 1] = b[j];
                b[m] = a;
            }
        }
    }
    printf("%d", ans);
    return 0;
}

烏龜棋

題目背景

小明過生日的時候,爸爸送給他一副烏龜棋當作禮物。

題目描述

烏龜棋的棋盤是一行N個格子,每個格子上一個分數(非負整數)。棋盤第1格是唯一的起點,第N格是終點,游戲要求玩家控制一個烏龜棋子從起點出發走到終點。

烏龜棋中M張爬行卡片,分成4種不同的類型(M張卡片中不一定包含所有4種類型的卡片,見樣例),每種類型的卡片上分別標有1、2、3、4四個數字之一,表示使用這種卡片后,烏龜棋子將向前爬行相應的格子數。游戲中,玩家每次需要從所有的爬行卡片中選擇一張之前沒有使用過的爬行卡片,控制烏龜棋子前進相應的格子數,每張卡片只能使用一次

游戲中,烏龜棋子自動獲得起點格子的分數,并且在后續的爬行中每到達一個格子,就得到該格子相應的分數。玩家最終游戲得分就是烏龜棋子從起點到終點過程中到過的所有格子的分數總和。

很明顯,用不同的爬行卡片使用順序會使得最終游戲的得分不同,小明想要找到一種卡片使用順序使得最終游戲得分最多。

現在,告訴你棋盤上每個格子的分數和所有的爬行卡片,你能告訴小明,他最多能得到多少分嗎?

輸入輸出格式

輸入格式:
輸入文件的每行中兩個數之間用一個空格隔開。

第1行2個正整數N和M,分別表示棋盤格子數和爬行卡片數。

第2行N個非負整數,a1a2……aN,其中ai表示棋盤第i個格子上的分數。

第3行M個整數,b1b2……bM,表示M張爬行卡片上的數字。

輸入數據保證到達終點時剛好用光M張爬行卡片。

輸出格式:
輸出只有1行,1個整數,表示小明最多能得到的分數。

輸入輸出樣例

輸入樣例#1:

9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1

輸出樣例#1:

73

說明

每個測試點1s

小明使用爬行卡片順序為1,1,3,1,2,得到的分數為6+10+14+8+18+17=73。注意,由于起點是1,所以自動獲得第1格的分數6。

數據范圍

對于30%的數據有1≤N≤30,1≤M≤12。
對于50%的數據有1≤N≤120,1≤M≤50,且4種爬行卡片,每種卡片的張數不會超過20。
對于100%的數據有1≤N≤350,1≤M≤120,且4種爬行卡片,每種卡片的張數不會超過40;0≤ai≤100,1≤i≤N;1≤bi≤4,1≤i≤M。

思路

  1. 這種分步決策的問題先考慮動態規劃。
  2. 由于有4種不同的卡片,所以我們肯定要在狀態上把這個反映出來,要不然顯然轉移不了狀態。
  3. 還有一點就是我們知道了四種卡片用了的數量我們自然就可以知道走了多少步,這樣一來問題就很簡單了。
  • f[x1][x2][x3][x4]表示四種卡片分別用了x1,x2,x3,x4張時的最大得分
  • 容易得到f[x1][x2][x3][x4]=max(f[x1-1][x2][x3][x4],f[x1][x2-1][x3][x4],f[x1][x2][x3-1][x4],f[x1][x2][x3][x4-1])+a[x1+x22+x33+x4*4]

代碼

#include  <cstdio>
#include  <iostream>
#include  <cstring>
using namespace std;
inline int max(int a, int b) {
    if (a > b) return a;
    else return b;
}
int f[42][42][42][42], i, n, m, num[5], a[400], k, l1, l2, l3, l4;
int main()
{
    scanf("%d%d", &n, &m);
    for (i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    memset(num, 0, sizeof(num));
    for (i = 1; i <= m; i++)
    {
        scanf("%d", &k);
        num[k]++;
    }
    memset(f, 0, sizeof(0));
    int t;
    f[0][0][0][0] = a[1];
    for (l1 = 0; l1 <= num[1]; l1++)
        for (l2 = 0; l2 <= num[2]; l2++)
            for (l3 = 0; l3 <= num[3]; l3++)
                for (l4 = 0; l4 <= num[4]; l4++)
                {
                    t = l1 + l2 * 2 + l3 * 3 + l4 * 4 + 1;
                    if (l1)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1 - 1][l2][l3][l4] + a[t]);
                    if (l2)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2 - 1][l3][l4] + a[t]);
                    if (l3)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2][l3 - 1][l4] + a[t]);
                    if (l4)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2][l3][l4 - 1] + a[t]);
                }
    printf("%d", f[num[1]][num[2]][num[3]][num[4]]);
}

關押罪犯

題目描述

S 城現有兩座監獄,一共關押著N 名罪犯,編號分別為1~N。他們之間的關系自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發沖突。我們用“怨氣值”(一個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為c 的罪犯被關押在同一監獄,他們倆之間會發生摩擦,并造成影響力為c 的沖突事件。
每年年末,警察局會將本年內監獄中的所有沖突事件按影響力從大到小排成一個列表,然后上報到S 城Z 市長那里。公務繁忙的Z 市長只會去看列表中的第一個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。
在詳細考察了N 名罪犯間的矛盾關系后,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的沖突事件影響力都較小,從而保住自己的烏紗帽。假設只要處于同一監獄內的某兩個罪犯間有仇恨,那么他們一定會在每年的某個時候發生摩擦。
那么,應如何分配罪犯,才能使Z 市長看到的那個沖突事件的影響力最小?這個最小值是多少?

輸入輸出格式

輸入格式:
輸入文件的每行中兩個數之間用一個空格隔開。第一行為兩個正整數N 和M,分別表示罪犯的數目以及存在仇恨的罪犯對數。接下來的M 行每行為三個正整數aj,bj,cj,表示aj 號和bj 號罪犯之間存在仇恨,其怨氣值為cj。數據保證1<aj=<=bj<=N ,0 < cj≤ 1,000,000,000,且每對罪犯組合只出現一次。

輸出格式:
共1 行,為Z 市長看到的那個沖突事件的影響力。如果本年內監獄中未發生任何沖突事件,請輸出0。

輸入輸出樣例
輸入樣例#1:

4 61 4 25342 3 35121 2 283511 3 66182 4 18053 4 12884

輸出樣例#1:

3512

說明

【輸入輸出樣例說明】罪犯之間的怨氣值如下面左圖所示,右圖所示為罪犯的分配方法,市長看到的沖突事件影響力是3512(由2 號和3 號罪犯引發)。其他任何分法都不會比這個分法更優。



【數據范圍】對于30%的數據有N≤ 15。對于70%的數據有N≤ 2000,M≤ 50000。對于100%的數據有N≤ 20000,M≤ 100000。

代碼

#include <algorithm>
#include <iostream>
using namespace std;
int n,m,f[40001],x,y;
struct data
{
int a,b,c;
}e[100001];
int gz(const data &a,const data &b){
    if(a.c>b.c)return 1;
    else return 0;
}
int find(int x) {
    return f[x]==x?x:f[x]=find(f[x]);
}
int main() {
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        cin>>e[i].a>>e[i].b>>e[i].c;
    for(int i=1;i<=n*2;i++)
        f[i]=i;
    sort(e+1,e+m+1,gz);
    for(int i=1;i<=m;i++) 
    {
        x=find(e[i].a);
        y=find(e[i].b);
        if(x==y) 
        {
            cout<<e[i].c;
            return 0;
        }
        f[y] = find(e[i].a + n);
        f[x] = find(e[i].b + n);
    }
    cout<<0;
    return 0;
}

引水入城

題目描述

在一個遙遠的國度,一側是風景秀美的湖泊,另一側則是漫無邊際的沙漠。該國的行政 區劃十分特殊,剛好構成一個N行M列的矩形,如上圖所示,其中每個格子都代表一座城 市,每座城市都有一個海拔高度。 為了使居民們都盡可能飲用到清澈的湖水,現在要在某些城市建造水利設施。水利設施 有兩種,分別為蓄水廠和輸水站。蓄水廠的功能是利用水泵將湖泊中的水抽取到所在城市的 蓄水池中。因此,只有與湖泊毗鄰的第1行的城市可以建造蓄水廠。而輸水站的功能則是通 過輸水管線利用高度落差,將湖水從高處向低處輸送。故一座城市能建造輸水站的前提,是 存在比它海拔更高且擁有公共邊的相鄰城市,已經建有水利設施。 由于第N行的城市靠近沙漠,是該國的干旱區,所以要求其中的每座城市都建有水利 設施。那么,這個要求能否滿足呢?如果能,請計算最少建造幾個蓄水廠;如果不能,求干 旱區中不可能建有水利設施的城市數目。

輸入輸出

輸入描述 Input Description

輸入的每行中兩個數之間用一個空格隔開。 輸入的第一行是兩個正整數N和M,表示矩形的規模。 接下來N行,每行M個正整數,依次代表每座城市的海拔高度。

輸出描述 Output Description

輸出有兩行。如果能滿足要求,輸出的第一行是整數1,第二行是一個整數,代表最少 建造幾個蓄水廠;如果不能滿足要求,輸出的第一行是整數0,第二行是一個整數,代表有 幾座干旱區中的城市不可能建有水利設施。

樣例

樣例輸入 Sample Input

2 5
9 1 5 4 3
8 7 6 1 2

樣例輸出 Sample Output

1
1

數據范圍及提示 Data Size & Hint

數據范圍
本題共有10個測試數據,每個數據的范圍如下表所示: 測試數據編號 能否滿足要求 N M 1 不能 ≤ 10 ≤ 10 2 不能 ≤ 100 ≤ 100 3 不能 ≤ 500 ≤ 500 4 能 = 1 ≤ 10 5 能 ≤ 10 ≤ 10 6 能 ≤ 100 ≤ 20 7 能 ≤ 100 ≤ 50 8 能 ≤ 100 ≤ 100 9 能 ≤ 200 ≤ 200 10 能 ≤ 500 ≤ 500 對于所有的10個數據,每座城市的海拔高度都不超過10^6

樣例2 說明

數據范圍

題解

顯然每個第一行的點要覆蓋最后一行連續的一段區間,如果有些點不能覆蓋,則這些點一定不能滿足

從上往下灌水得出每個最后一行的點是否能被覆蓋,回答第一問
從下往上反灌水得出每個第一行的點覆蓋區間的左右端點

區間排序做線段覆蓋

復雜度 O(nm+mlogm)

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mod 1000000007
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int xx[4]={1,-1,0,0},yy[4]={0,0,1,-1};
int n,m;
int a[505][505],mp[505][505],l[505][505],r[505][505];
int qx[250005],qy[250005];
struct data{int l,r;}c[505];
bool operator<(data a,data b)
{
    return a.l==b.l?a.r<b.r:a.l<b.l;
}
void bfs(int b[505][505],int x,int y,int v,bool f)
{
    int head=0,tail=1;
    qx[0]=x;qy[0]=y;b[x][y]=v;
    while(head!=tail)
    {
        int x=qx[head],y=qy[head];head++;
        for(int k=0;k<4;k++)
        {
            int nowx=x+xx[k],nowy=y+yy[k];
            if(nowx<1||nowy<1||nowx>n||nowy>m||b[nowx][nowy])continue;
            if(f==0&&a[nowx][nowy]>=a[x][y])continue;
            if(f==1&&a[nowx][nowy]<=a[x][y])continue;
            b[nowx][nowy]=b[x][y];
            qx[tail]=nowx;qy[tail]=nowy;tail++;
        }
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[i][j]=read();
    for(int i=1;i<=m;i++)
        bfs(mp,1,i,1,0);
    int tot=0;
    for(int i=1;i<=m;i++)
        if(!mp[n][i])tot++;
    if(tot)
    {
        printf("0\n%d\n",tot);
        return 0;
    }
    for(int i=1;i<=m;i++)if(!l[n][i])bfs(l,n,i,i,1);
    for(int i=m;i;i--)if(!r[n][i])bfs(r,n,i,i,1);
    for(int i=1;i<=m;i++)
    {
        if(!l[1][i])l[1][i]=r[1][i];
        if(!r[1][i])r[1][i]=l[1][i];
        c[i].l=l[1][i];c[i].r=r[1][i];
    }
    int now=0,to=0;
    sort(c+1,c+m+1);
    for(int i=1;i<=m;i++)
    {
        if(now+1>=c[i].l)to=max(to,c[i].r);
        else now=to,to=max(to,c[i].r),tot++;
    }
    if(now!=m)tot++;
    printf("1\n%d\n",tot);
    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 生活大爆炸版石頭剪刀布 題目描述 石頭剪刀布是常見的猜拳游戲:石頭勝剪刀,剪刀勝布,布勝石頭。如果兩個人出拳一樣,...
    bbqub閱讀 480評論 0 0
  • 今天推薦給大家一款“InkHunter”應用,通過AR技術實現“無痛”體驗紋身。 首次打開應用,使用者需要先進行一...
    幻眼科技閱讀 495評論 1 0
  • 2012年的夏天,炎熱、酷曬,我的心卻如巖漿一樣火熱,因為這一年經過痛苦的復讀我終于還是考進了不錯的大學,同樣,在...
    百里杜鵑花海閱讀 253評論 0 1