NOIP2009題解

spy:
題目大意:給定一串加密信息和原信息,讓你求出該加密信息是否滿足26個字母都存在,且加密信息中每個字母在原信息中對應不同的字母,原信息中的每個字母在加密信息中對應不同的字母,若不滿足輸出Failed,否則翻譯指定的加密信息。字符串長度<=100.
題解:一道簡單的字符串模擬題……看懂題目就好了,值得注意的是在樣例中沒有加密信息中的每個字母在原信息中對應不同的字母,題目看清楚了則無大礙。時間複雜度:O(len*26),空間複雜度:O(len)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int i,n1,n2,n3,j;
char s1[110],s2[110],s3[110],a[30];
bool f[30];
int main(){
    freopen("spy.in","r",stdin);
    freopen("spy.out","w",stdout);
    scanf("%s",s1);
    scanf("%s",s2);
    scanf("%s",s3);
    n1=strlen(s1);
    n3=strlen(s3);
    for(i=0;i<n1;i++)f[s2[i]-'A']=1;
    for(i=0;i<26;i++)
        if(!f[i]){
            puts("Failed");
            return 0;
        }
    for(i=0;i<n1;i++)
        if(!a[s1[i]-'A'])a[s1[i]-'A']=s2[i];
        else
         if(a[s1[i]-'A']!=s2[i]){
            puts("Failed");
            return 0;
         }
    for(i=0;i<25;i++)
        for(j=i+1;j<26;j++)
            if(a[i]==a[j]){
                puts("Failed");
                return 0;
            }
    for(i=0;i<n3;i++)putchar(a[s3[i]-'A']);
    return 0;
}

son:
題目大意:對於n組數據求滿足與a0的最大公因數爲a1,與b0的最小公倍數是b1的數的個數。n<=2000,a0,a1,b0,b1<=2*1e9.
題解:
顯然x是b1的約數a1的倍數吧……
一開始還以爲這題很神……要用什麼高超的方法……然而……
某道叫反質數的省選題可以證明b1的約數個數不會大於3000……這裏就不寫證明了……
於是我們只需要找出所有b1的約數,再一個個判斷即可……
那麼我們只需要O(b1 )求出所有質因數,再枚舉每個質因數需要選多少個,得到所有約數即可。
時間複雜度:O(2logb1 *n),空間複雜度:O(約數個數)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int pri[1010],i,j,n,a0,b0,a1,b1,a[1010],w,x[1000010],ans;
inline void chai(int x){
    for(int i=2;(long long)i*i<=x;i++)
        if(!(x%i)){
            pri[++a[0]]=i;
            while(!(x%i))a[a[0]]++,x/=i;
        }
    if(x>1)pri[++a[0]]=x,a[a[0]]=1;
}
inline int gcd(int x,int y){
    while(y){
        int t=x%y;
        x=y;
        y=t;
    }
    return x;
}
void dfs(int i,int j){
    if(i>a[0]){
        x[++w]=j;
        return;
    }
    dfs(i+1,j);
    for(int k=1;k<=a[i];k++){
        j*=pri[i];
        dfs(i+1,j);
    }
}
int main(){
    freopen("son.in","r",stdin);
    freopen("son.out","w",stdout);
    scanf("%d",&n);
    for(;n;n--){
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        memset(a,0,sizeof(a));
        w=0;
        ans=0;
        chai(b1);
        dfs(1,1);
        for(i=1;i<=w;i++)
            if(gcd(x[i],a0)==a1&&(long long)x[i]*b0/gcd(x[i],b0)==b1)ans++;
        printf("%d\n",ans);
    }
    return 0;
}

trade:
題目大意:有n個城市,有些城市之間有有向邊,有些城市之間有無向邊,每個城市對水晶球有一個固定價格,你現在需要找到一條從1~n的路從i買入從j賣出(j可以等於i)求賣出價-買入價最大值。n<=100000,m<=500000.
題解:
很顯然我們可以用一遍最短路得到從1~i的最小价對吧?只需把最短路中的d[i]=d[pos]+a[i][pos]改爲d[i]=min(d[pos],a[i])即可。同理,我們只需將邊反向,即可得到i~n的最大價格,於是,我們對於所有的i,只需要用賣出價-買入價即可。
其實這相當於是在求一個前綴最小值和後綴最大值……只不過是在圖上求,於是我們將普通的序列上的求法改爲最短路即可……時間複雜度:O(n log n+m log m),空間複雜度:O(m log m)。
sudoku:
題目大意:一個靶形數獨,上面已經填了若干個數,越接近靶心的數分值基數越高,一個數填進去的分值爲該數*分值基數,求填完數獨的最大分值和,若數獨無解輸出-1。已填的數不少於24個。
題解:
對於這種題目,最直觀的想法自然是爆搜……枚舉每個還沒填數的格子填什麼數,然後一直搜下去即可……如果這麼正着搜的話只有70分……倒着搜有90分(倒着搜的分數高是因爲出題人的疏忽……)
正確的做法應該這麼玩……
首先想想我們自己玩數獨的時候會怎麼操作?
顯然是先去那些已填的數最多的九宮格、行、或列開始填數對吧?
但是我們沒法每填一個數就判斷一下還沒填的數應該填哪一個……(這個其實我們可以用IDA*解決,但這裏不講)於是我們只能先預判一下哪些數要先填。
然後枚舉填什麼數的話可以用位運算加速處理。這樣的話就可以解決了。時間複雜度:O(9^(81-2*n)),空間複雜度:O(n)。

//位壓+按每行每列每九宮格需要填的數的個數排序 
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int sodu[9][9]={{1,1,1,2,2,2,3,3,3},
                {1,1,1,2,2,2,3,3,3},
                {1,1,1,2,2,2,3,3,3},
                {4,4,4,5,5,5,6,6,6},
                {4,4,4,5,5,5,6,6,6},
                {4,4,4,5,5,5,6,6,6},
                {7,7,7,8,8,8,9,9,9},
                {7,7,7,8,8,8,9,9,9},
                {7,7,7,8,8,8,9,9,9}},
    pnt[20][20],i,j,a[20][20],ans,n,cnt,h[20],l[20],s[20],hang[20],lie[20],sod[20];
struct node{
    int x,y;
    bool friend operator <(node a,node b){
        if(hang[a.x]==hang[b.x]){
            if(lie[a.y]==lie[b.y])return sod[sodu[a.x][a.y]]<sod[sodu[b.x][b.y]];
            return lie[a.y]<lie[b.y];
        }
        return hang[a.x]<hang[b.x];
    }
}nd[60];
bool flag;
void dfs(int k,int now){
    if(!k){
        ans=max(ans,now);
        flag=1;
        return;
    }
    int x=nd[k].x,y=nd[k].y;
    for(int i=1;i<=9;i++)
        if(h[x]&l[y]&s[sodu[x][y]]&1<<(i-1)){
            h[x]^=1<<(i-1);
            l[y]^=1<<(i-1);
            s[sodu[x][y]]^=1<<(i-1);
            dfs(k-1,now+pnt[x][y]*i);
            h[x]^=1<<(i-1);
            l[y]^=1<<(i-1);
            s[sodu[x][y]]^=1<<(i-1);
        }
}
int main(){
    freopen("sudoku.in","r",stdin);
    freopen("sudoku.out","w",stdout);
    for(i=0;i<9;i++)
        for(j=0;j<9;j++)
            pnt[i][j]=min(min(i,8-i),min(j,8-j))+6;
    for(i=0;i<9;i++)h[i]=l[i]=(1<<9)-1;
    for(i=1;i<10;i++)s[i]=(1<<9)-1;
    for(i=0;i<9;i++)
        for(j=0;j<9;j++){
            scanf("%d",&a[i][j]);
            if(a[i][j]==0)nd[++n].x=i,nd[n].y=j;
            else h[i]^=1<<(a[i][j]-1),l[j]^=1<<(a[i][j]-1),s[sodu[i][j]]^=1<<(a[i][j]-1),cnt+=pnt[i][j]*a[i][j],hang[i]++,lie[j]++,sod[sodu[i][j]]++;
        }
    ans=cnt;
    sort(nd+1,nd+1+n);
    dfs(n,ans);
    if(!flag)puts("-1");
    else printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章