1. 程式人生 > 其它 >NOIP2008 提高組題解

NOIP2008 提高組題解

luogu對應題目T1 笨小猴T2 火柴棒等式T3 傳紙條T4 雙棧排序 ,可以到luogu上檢視更多題解。

T1 笨小猴 素數

給出一個單詞,統計其中出現最多的字母出現的次數maxn,以及出現最少的字母的次數minn,如果maxn-minn是質數的話則作為一個Lucky Word..否則即為No Answer.
直接模擬即可

#include <stdio.h>
#include <string.h>
 
int count[27],max,min;
char s[200];
bool notprime[102];
 
int main()
{
	freopen("word.in","r",stdin);
	freopen("word.out","w",stdout);
	gets(s);
	memset(count,0,sizeof(count));
	for (int i=0;s[i];i++)
		count[s[i]-96]++;
	min = 2147483647;
	max = 0;
	for (int i=1;i<=26;i++)
	{
		if (!count[i])
			continue;
		if (max<count[i])
			max = count[i];
		if (min>count[i])
			min = count[i];
	}
	int j;
	notprime[0] = true;
	notprime[1] = true;
	for (int i=2;i<=50;i++)
	{
		j = i*2;
		while (j<=100)
		{
			notprime[j] = true;
			j += i;
		}
	}
	max -= min;
	if (notprime[max])
	{
		printf("No Answer\n");
		max = 0;
	}
	else
		printf("Lucky Word\n");
	printf("%d\n",max);
	return 0;
}

T2 火柴棒等式 列舉

給你n(n<=24)根火柴棒,叫你拼出 "A + B = C"這樣的等式,求方案數.
直接列舉A和B(事實證明只到3位數),事先預處理2000以內各個數所用的火柴數.直接枚舉出解

#include <stdio.h>
#include <string.h>
 
int n,ans;
int c[2002]={6,2,5,5,4,5,6,3,7,6};
 
int main()
{
	freopen("matches.in","r",stdin);
	freopen("matches.out","w",stdout);
	scanf("%d",&n);
	for (int i=10;i<=1800;i++)
		c[i] = c[i/10]+c[i%10];
	n -= 4;ans = 0;
	for (int i=0;i<=800;i++)
		for (int j=0;j<=800;j++)
			if (c[i]+c[j]+c[i+j]==n)
				ans++;
	printf("%d\n",ans);
	return 0;
}

T3 傳紙條 動態規劃

給一個矩陣(左上角和右下角固定為0),從左上角走兩次到右下角,兩次走的路徑不能有交集(即一個點不能被走兩次),求兩次走過的格子上的數的和最大是多少.(類似二取方格數.)
二取方格數很經典的題目了,於是便直接以 f[i][j][k][p] 表示第一條路徑走到(i,j),第二條路徑走到(k,p)所取到的數的最大值..轉移方程就很好辦了..同時注意判斷兩條路不要從同一個點轉移過來就好了.

#include <stdio.h>
#include <string.h>
 
int a[52][52],f[52][52][52][52],n,m,ni,nj,nk,np;
int next[4][4]={0,-1,-1,0,-1,0,-1,0,0,-1,0,-1,-1,0,0,-1};
 
int main()
{
	freopen("message.in","r",stdin);
	freopen("message.out","w",stdout);
	scanf("%d %d",&n,&m);
	memset(f,0,sizeof(f));
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			for (int k=1;k<=n;k++)
				for (int p=j;p<=m;p++)
					for (int m=0;m<=3;m++)
					{
						ni = i + next[m][0];
						nj = j + next[m][1];
						nk = k + next[m][2];
						np = p + next[m][3];
						if ((ni!=nk)||(nj!=np))
							if (f[i][j][k][p] < f[ni][nj][nk][np] + a[i][j] + a[k][p])
								f[i][j][k][p] = f[ni][nj][nk][np] + a[i][j] + a[k][p];
					}
	printf("%d",f[n][m][n][m]);
	return 0;
}

T4 雙棧排序 二分圖、棧

本題很難,有如下題意。
有兩個佇列和兩個棧,分別命名為佇列1(q1),佇列2(q2),棧1(s1)和棧2(s2).最初的時候,q2,s1和s2都為空,而q1中有n個數(n<=1000),為1~n的某個排列.
現在支援如下四種操作:
a操作,將 q1的首元素提取出並加入s1的棧頂.
b操作,將s1的棧頂元素彈出並加入q1q2的佇列尾.
c操作,將 q1的首元素提取出並加入s2的棧頂.
d操作,將s2的棧頂元素彈出並加入q1q2的佇列尾.
請判斷,是否可以經過一系列操作之後,使得q2中依次儲存著1,2,3,…,n.如果可以,求出字典序最小的一個操作序列.
下面開始做題了
第一步需要解決的問題是,判斷是否有解.
考慮對於任意兩個數q1[i]和q1[j]來說,它們不能壓入同一個棧中的充要條件是什麼(注意沒有必要使它們同時存在於同一個棧中,只是壓入了同 一個棧).實際上,這個條件p是:存在一個k,使得i<j<k且q1[k]<q1[i]<q1[j].
首先證明充分性,即如果滿足條件p,那麼這兩個數一定不能壓入同一個棧.這個結論很顯然,使用反證法可證.
假設這兩個數壓入了同一個棧,那麼在壓入q1[k]的時候棧內情況如下:
…q1[i]…q1[j]…
因為q1[k]比q1[i]和q1[j]都小,所以很顯然,當q1[k]沒有被彈出的時候,另外兩個數也都不能被彈出(否則q2中的數字順序就不是1,2,3,…,n了).
而之後,無論其它的數字在什麼時候被彈出,q1[j]總是會在q1[i]之前彈出.而q1[j]>q1[i],這顯然是不正確的.
接下來證明必要性.也就是,如果兩個數不可以壓入同一個棧,那麼它們一定滿足條件p.這裡我們來證明它的逆否命題,也就是"如果不滿足條件p,那麼這兩個數一定可以壓入同一個棧."
不滿足條件p有兩種情況:一種是對於任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一種是對於任意i<j,q1[i]>q1[j].
第一種情況下,很顯然,在q1[k]被壓入棧的時候,q1[i]已經被彈出棧.那麼,q1[k]不會對q1[j]產生任何影響(這裡可能有點亂,因為看起來,當q1[j]<q1[k]的時候,是會有影響的,但實際上,這還需要另一個數r,滿足j<k<r且 q1[r]<q1[j]<q1[k],也就是證明充分性的時候所說的情況…而事實上我們現在並不考慮這個r,所以說q1[k]對q1[j]沒有影響).="">
第二種情況下,我們可以發現這其實就是一個降序序列,所以所有數字都可以壓入同一個棧.
這樣,原命題的逆否命題得證,所以原命題得證.</q1[k]的時候,是會有影響的,但實際上,這還需要另一個數r,滿足j<k<r且>
此時,條件p為q1[i]和q1[j]不能壓入同一個棧的充要條件也得證.
這樣,我們對所有的數對(i,j)滿足1<=i<j<=n,檢查是否存在i<j<k滿足p1[k]< p1[i]

#include<bits/stdc++.h>
using namespace std;

const int maxn=1005;
int a[maxn],color[maxn];
int s1[maxn],s2[maxn];
int n;
vector< vector<int> >G(maxn);
bool bfs(int s)
{
    queue<int>q;
    color[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(color[v]==0)color[v]=color[u]*-1,q.push(v);
            else if(color[v]!=color[u]*-1)return false;
        }
    }
    return true;
}
int main()
{
	freopen("twostack.in","r",stdin);
	freopen("twostack.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int r=0;
        for(int j=n;j>i;j--)
            if(a[i]>a[j])
            {
                r=j;
                break;
            }
        for(int j=i+1;j<r;j++)
            if(a[i]<a[j])
            {
                G[i].push_back(j);
                G[j].push_back(i);
            }
    }
    int ok=1;
    for(int i=1;i<=n;i++)
        if(color[i]==0)
        {
            if(bfs(i))continue;
            ok=0;
            break;
        }
    if(ok==0)
    {
        printf("0");
        return 0;
    }
    int cnt=1,tp1=0,tp2=0;
    vector<char>v;
    for(int i=1;i<=n;i++)
    {
        if(color[i]==1)
        {
            s1[++tp1]=a[i];
            v.push_back('a');
        }
        else
        {
            s2[++tp2]=a[i];
            v.push_back('c');
        }
        while(s1[tp1]==cnt||s2[tp2]==cnt)
        {
            if(s1[tp1]==cnt)
            {
                tp1--;
                v.push_back('b');
            }
            else
            {
                tp2--;
                v.push_back('d');
            }
            cnt++;
        }
    }
    printf("%c",v[0]);
    for(int i=1;i<v.size();i++)
        printf(" %c",v[i]);
    return 0;
}
大佬您太強了,還請多多指教哎