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;
}
大佬您太強了,還請多多指教哎