申請SAE

如果您發現本博客的外觀很難看,那是因為部分外觀文件被中國.國家.防火.牆屏.蔽所致!
請翻~牆!

我的Wordpress博客的地址: http://zhuyf.tk/

2011年11月5日 星期六

[貪心策略]USACO Open07 Protecting the Flowers (flowers)解題報告

【題目描述】
Farmer John went to cut some wood and left N (2 ≤ N ≤ 100,000) cows eating the grass, as usual. When he returned, he found to his horror that the cluster of cows was in his garden eating his beautiful flowers. Wanting to minimize the subsequent damage, FJ decided to take immediate action and transport each cow back to its own barn.
Each cow i is at a location that is Ti minutes (1 ≤ Ti ≤ 2,000,000) away from its own barn. Furthermore, while waiting for transport, she destroys Di (1 ≤ Di ≤ 100) flowers per minute. No matter how hard he tries, FJ can only transport one cow at a time back to her barn. Moving cow i to its barn requires 2*Ti minutes (Ti to get there and Ti to return). FJ starts at the flower patch, transports the cow to its barn, and then walks back to the flowers, taking no extra time to get to the next cow that needs transport.
Write a program to determine the order in which FJ should pick up the cows so that the total number of flowers destroyed is minimized. Input
  • Line 1: A single integer N
  • Lines 2..N + 1: Each line contains two space-separated integers, Ti and Di, that describe a single cow's characteristics
Output
  • Line 1: A single integer that is the minimum number of destroyed flowers
Sample Input
6
3 1
2 5
2 3
3 2
4 1
1 6
Sample Output
86
Output Details
FJ returns the cows in the following order: 6, 2, 3, 4, 1, 5. While he is transporting cow 6 to the barn, the others destroy 24 flowers; next he will take cow 2, losing 28 more of his beautiful flora. For the cows 3, 4, 1 he loses 16, 12, and 6 flowers respectively. When he picks cow 5 there are no more cows damaging the flowers, so the loss for that cow is zero. The total flowers lost this way is 24 + 28 + 16 + 12 + 6 = 86.

【分析】
這道題是貪心策略,我們可以用一個C結構儲存每頭奶牛的T、D。
貪心策略就是找出C[i].T/C[i].D的最小值,這可以用快速排序來實現。
大家知道除法運算時很慢的,為了提高速度,我們可以把 快速排序的換元函數中的   (C[i].T/C[i].D)<(C[j].T/C[j].D)  交叉相乘,改為 (C[i].T*C[j].D)<(C[j].T*C[i].D)。

快速排序後,位於數組首位的奶牛不毀壞花,所以從i=2 to N枚舉每頭牛破壞的花的個數,輸出即可。由於結果可能很大,最好用long long。

【我的代碼】 
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int N;
class COW
{
public:
    int speed;
    int eat;
}C[100001];

int cmp(const void *a,const void *b)
{
    class COW *c=(class COW *)a;
    class COW *d=(class COW *)b;
    int A=c->speed*d->eat;
    int B=d->speed*c->eat;
    if(A<B)
        return -1;
    else
        return 1;
}

void init()
{
    scanf("%d\n",&N);
    for (int i=1;i<=N;i++)
        scanf("%d %d\n",&C[i].speed,&C[i].eat);
    qsort(C+1,N,sizeof(C[0]),cmp);
    long long T=0,Des=0;
    for (int i=1;i<=N;i++)
    {
        Des+=(T*C[i].eat);
        T+=2*C[i].speed;
    }
    cout<<Des<<endl;
}

int main()
{
    freopen("flowers.in","r",stdin);
    freopen("flowers.out","w",stdout);
    init();
    return 0;
}

[最短路徑]NOIP模擬題:奇怪的電梯 lift 解題報告

問題描述
呵呵,有一天我做了一個夢,夢見了一種很奇怪的電梯。大樓的每一層樓都可以停電梯,而且第 i 層樓 (1<=i<=N) 上有一個數字 Ki(0<=Ki<=N) 。電梯只有四個按鈕:開,關,上,下。上下的層數等於當前樓層上的那個數字。當然,如果不能滿足要求,相應的按鈕就會失靈。例如: 3 3 1 2 5 代表了 Ki(K1=3,K2=3,……) ,從一樓開始。在一樓,按 “ 上 ” 可以到 4 樓,按 “ 下 ” 是不起作用的,因爲沒有 -2 樓。那麼,從 A 樓到 B 樓至少要按幾次按鈕呢?
輸入
輸入文件共有二行,第一行爲三個用空格隔開的正整數,表示 N,A,B(1≤N≤200, 1≤A,B≤N) ,第二行爲 N 個用空格隔開的正整數,表示 Ki 。
輸出
輸出文件僅一行,即最少按鍵次數 , 若無法到達,則輸出 -1 。
樣例
lift.in
5 1 5
3 3 1 2 5
lift.out
3

【分析】
做這題方法很多,可以用搜索,最短路也行。
我用的是Floyd算法,三層循環,很好寫,呵呵。
開一個Mat數組,表示鄰接矩陣,Mat[i,j]初始化為-1(i!=j),Mat[i,i]初始化為0。

讀入時,把每個樓層能到達的樓層置為1。
然後Floyd就行了。

最後輸出Mat[A][B]。

【我的代碼】
#include <iostream>
#include <cstdio>
using namespace std;
int A,B;
int Mat[201][201];
int N;

void init()
{
    scanf("%d %d %d\n",&N,&A,&B);
   
    for (int i=1;i<=N;i++)
        for (int j=1;j<=N;j++)
            Mat[i][j]=-1;
   
    int tmp;
    for (int i=1;i<=N;i++)
    {
        cin>>tmp;
        if(i-tmp>=1)
            Mat[i][i-tmp]=1;
        if(i+tmp<=N)
            Mat[i][i+tmp]=1;
    }
}

void Floyd() 

    int temp; 
    for (int k=1;k<=N;k++)   
    {   
        for(int i=1;i<=N;i++)   
        {   
            for(int j=1;j<=N;j++)   
            {   
                if ( Mat[i][k]!=-1 && Mat[k][j]!=-1)   
                {   
                    temp=Mat[i][k]+Mat[k][j];  
                    if ( (Mat[i][j]==-1) || ( Mat[i][j]>temp ) )   
                        Mat[i][j]=temp;   
                }   
            }   
        }   
    }   


int main()
{
    freopen("lift.in","r",stdin);
    freopen("lift.out","w",stdout);
    init();
    Floyd();
    if(A==B)
        cout<<0<<endl;
    else
        cout<<Mat[A][B]<<endl;
    return 0;
}

[動態規劃]NOIP模擬題:整理書本 book

【問題描述】
Ztc想把他滿屋子的書整理一下。爲了應付繁重的學習任務,ztc已經筋疲力盡了,於是他向你求助,請你幫他計算他最少需要花費多少力氣。
書本分成若干堆,呈直綫排布。每一堆的書本都有重量w和價值v。Ztc的任務是將所有書合成一堆。因爲Ztc很看重書本的價值,所以他認爲合併i,j兩堆的書所需要的力氣爲w[i]-v[i]+w[j]-v[j]。合併後的書堆的重量和價值均爲合併前兩堆書的重量和價值的總和。也就是說,合併i.j兩堆的書後,w=w[i]+w[j],v=v[i]+v[j]。小智個人不願意走來走去,所以合併只能在相鄰兩堆書本間進行。書本合併前後,位置不變。如將1,2,3中的1,2進行合併,那麼合併結果爲3,3,再將3,3合併爲6(1,2,3,6指重量)。
【輸入】
輸入文件book.in的第一行是一個整數n(2≤n≤400)。
第2~n+1行每行兩個整數w和v(0<v<w≤1000)。
【輸出】
輸出文件book.out共一行,這一行只有一個整數f,表示最小力氣。
【輸入輸出樣例】
book.in
3
6 5
9 7
11 2
book.out
15
【輸入輸出樣例解釋】
先將前兩堆合併,再將合併後的書堆與剩餘的一堆合併。
【限制】
30%的數據滿足:2≤n≤100
100%的數據滿足:2≤n≤400

【分析】
經典的石子歸併問題,動態規劃。

狀態設定:
       W[i]表示第i本書的重量
       V [i]表示第i本書的價值
       SumW[i,j]表示第i本書 到 第j本書 重量的總和
       SumV [i,j]表示第i本書 到 第j本書 價值的總和
       F[i,j]表示從第i本書 到 第j本書 合併的最小代價。
邊界條件: 
       F[i][i]=0  
狀態轉移方程:
      F[i,j]=min{F[i,k]+F[k+1][j]+SumW[i,j]-SumV[i,j]}(i<=k<=j-1)
目標結果:
      F[1][N]


【我的代碼】
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
#define M 401
#define INF 1000000000
int N;         

int f[M][M];   //f[i,j]表示從第i個到第j個歸併的最小代價
int sum1[M][M]; //sum[i,j]表示第i個數到第j個數W的總和
int sum2[M][M]; //sum[i,j]表示第i個數到第j個數V的總和
int stone1[M];  //書本的重量
int stone2[M];  //書本的重要度


int main()
{
    freopen("book.in","r",stdin);
    freopen("book.out","w",stdout);
    int i,j,k;
    cin>>N;
    for(i=1;i<=N;i++)
        scanf("%d %d\n",&stone1[i],&stone2[i]);

    for(i=1;i<=N;i++)
    {
        f[i][i]=0;
        sum1[i][i]=stone1[i];
        for(j=i+1;j<=N;j++)
            sum1[i][j]=sum1[i][j-1]+stone1[j];
    }

    for(i=1;i<=N;i++)
    {
        //f[i][i]=0;
        sum2[i][i]=stone2[i];
        for(j=i+1;j<=N;j++)
            sum2[i][j]=sum2[i][j-1]+stone2[j];
    }

   
    for(int len=2;len<=N;len++)
    {
        for(i=1;i<=N-len+1;i++)
        {
            j=i+len-1;
            f[i][j]=INF;  
            for(k=i;k<=j-1;k++)
            {
                if(f[i][j]>f[i][k]+f[k+1][j]+sum1[i][j]-sum2[i][j])
                    f[i][j]=f[i][k]+f[k+1][j]+sum1[i][j]-sum2[i][j];
            }
        }
    }
    printf("%d\n",f[1][N]);
    return 0;
}

 

[動態規劃]NOIP模擬題:逛街 shop 解題報告

【問題描述】
某天,zcL在街上閒逛。他在超市裏看到促銷廣告:商品大降價。於是他很高興地拿着籃子購物去了。
已知商場內有n種商品。每種商品的重量爲w千克,價格爲v,價值爲t。此種商品有h件。
注意:此商場有一個奇怪的規定。每種物品要麼不買,要麼買1件或h件。ZCL帶了y元。ZCL最多能扛x千克的物品。請幫ZCL求出他最多能獲得的價值。(不允許搶劫)
【輸入】
輸入文件shop.in的第一行有3個用空格隔開的整數n、x和y。
接下來的n行,每行有4個數據,分別爲w,v,t和h。
【輸出】
輸出文件shop.out共一行,表示ZCL最多能獲得的價值。
【輸入輸出樣例】
shop.in
2 8 10
5 3 7 1
3 7 10 1
shop.out。
17
【限制】
100%的數據滿足:0≤n≤300.0≤x≤1000,0≤y≤100,0≤h≤10


【分析】
這是一個多維的0/1背包問題,動態規劃。
由於這個背包有三維,所以開三維數組會爆掉的。
我們可以採取邊讀邊算的方式把數組降為二維。

狀態設定
     F[i,j] 重量為i千克,花費為j元時的最大價值。
邊界條件
     F[i,j]=0 (0<=i<=x,0<=j<=y)
狀態轉移方程
     F[i,j]=Max{F[i,j] , F[i-w,j-v]+t , F[i-w*h,j-v*h]+t*h}
目標結果:
     F[x][y]

【我的代碼】
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
int N;
int X,Y;
int W,V,T,H;
int F[1001][101];

int Max(int a,int b)
{
    return a>b?a:b;
}

int main()
{
    freopen("shop.in","r",stdin);
    freopen("shop.out","w",stdout);
    scanf("%d %d %d\n",&N,&X,&Y);
   
    for(int i=1;i<=N;i++)
    {
        scanf("%d %d %d %d\n",&W,&V,&T,&H);
        for (int i=X;i>=W;i--)
        {
            for (int j=Y;j>=V;j--)
            {
                F[i][j]=Max(F[i][j],F[i-W][j-V]+T);
                if(i-W*H>=0 && j-V*H>=0)
                    F[i][j]=Max(F[i][j],F[i-W*H][j-V*H]+T*H);
            }
        }
    }
   
    printf("%d\n",F[X][Y]);
    return 0;
}

[搜索]USACO Oct09 Bessie體重問題 diet 解題報告

【問題描述】
Bessie像她的諸多姊妹一樣,因爲從Farmer John的草地吃了太多美味的草而長出了太多的
贅肉。所以FJ將她置於一個及其嚴格的節食計畫之中。她每天不能吃多過H (5 <= H <=
45,000)公斤的乾草。
Bessie只能吃一整捆乾草;當她開始吃一捆乾草的之後就再也停不下來了。她有一個完整的
N (1 <= N <= 500)捆可以給她當作晚餐的乾草的清單。她自然想要儘量吃到更多的乾草。
很自然地,每捆乾草只能被吃一次(即使在列表中相同的重量可能出現2次,但是這表示的是
兩捆乾草,其中每捆乾草最多只能被吃掉一次)。
給定一個列表表示每捆乾草的重量S_i (1 <= S_i <= H), 求Bessie不超過節食的限制的
樣例輸入 (文件 diet.in):
56 4
15
19
20
21
輸入細節:
有四捆草,重量分別是15, 19, 20和21。Bessie在56公斤的限制範圍內想要吃多少就可
以吃多少。
輸出格式:
* 第一行: 一個單獨的整數表示Bessie在限制範圍內最多可以吃多少公斤的乾草。
樣例輸出 (文件 diet.out):
56
輸出細節:
Bessie可以吃3捆乾草(重量分別爲15, 20, 21)。恰好達到她的56公斤的限制。

【分析】
學過OI的人都能看出來,這是一個簡單的搜索題目,由於數據不大,廣度優先搜索即可快速過全數據,無需太多優化。

由於每捆乾草只能用一次,所以有人想在廣搜隊列中開一個bool 數組,用來記錄哪些乾草被吃過了。但是,這樣時間複雜度、空間複雜度都會大大增加,有超時、超出內存限制的風險。

其實,我們可以這樣做防止重複。在隊列中記錄上一次吃的是哪一捆乾草,然後下一次搜索時只需要從上一次乾草的後面開始搜索即可,即吃的乾草的編號構成上升序列,這樣不僅能保證不重複,還能提高程序運行效率。

【我的代碼】
 #include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int W[501];
int L;
int N;
int M=0;

void init()
{
    scanf("%d %d\n",&L,&N);
    for (int i=1;i<=N;i++)
        scanf("%d\n",&W[i]);
}

class QUEUE
{
public:
    int cur;//當前捆的標號
    int now;//當前草的重量   
}Q[1000000];

void bfs()
{
    int big=1;
    int sma=0;
    Q[0].cur=0;
    Q[0].now=0;
    int Tc,Tn;
    while(sma<=big)
    {
        Tc=Q[sma].cur;
        Tn=Q[sma].now;
        for (int i=Tc+1;i<=N;i++)
        {
            if(W[i]+Tn>L)
            {
                if(Tn>M)
                    M=Tn;
                continue;
            }
            if(W[i]+Tn==L)
            {
                M=L;
                return;
            }
            Q[big].cur=i;
            Q[big].now=W[i]+Tn;
            big++;
        }
        sma++;
    }
    return;
}

int main()
{
    freopen("diet.in","r",stdin);
    freopen("diet.out","w",stdout);
    init();
    bfs();
    printf("%d\n",M);
    return 0;
}

2011年11月4日 星期五

[搜索]USACO Mar08 麻煩的乾草打包機 baler 解題報告

【問題描述】
Farmer John新買的乾草打包機的內部結構大概算世界上最混亂的了,它不象普通的機器一樣有明確的內部傳動裝置,而是,N (2 <= N <= 1050)個齒輪互相作用,每個齒輪都可能驅動着多個齒輪。
FJ記錄了對於每個齒輪i,記錄了它的3個參數:X_i,Y_i表示齒輪中心的位置座標(-5000 <= X_i <= 5000; -5000 <= Y_i <= 5000);R_i表示該齒輪的半徑(3 <= R_i <= 800)。驅動齒輪的位置爲0,0,並且FJ也知道最終的工作齒輪位於X_t,Y_t。
驅動齒輪順時針轉動,轉速爲10,000轉/小時。你的任務是,確定傳動序列中所有齒輪的轉速。傳動序列的定義爲,能量由驅動齒輪傳送到 工作齒輪的過程中用到的所有齒輪的集合。對能量傳送無意義的齒輪都應當被忽略。在一個半徑爲Rd,轉速爲S轉/每小時的齒輪的帶動下,與它相接的半徑爲 Rx的齒輪的轉速將爲-S*Rd/Rx轉/小時。S前的負號的意思是,一個齒輪帶動的另一個齒輪的轉向會與它的轉向相反。

 FJ只對整個傳動序列中所有齒輪速度的絕對值之和感興趣,你的任務也就相應轉化成求這個值。機器中除了驅動齒輪以外的所有齒輪都被另外某個齒輪帶動,並且不會出現2個不同的齒輪帶動同一個齒輪的情況。
相信你能輕易地寫個程序來完成這些計算
程序名: baler
輸入格式:
第1行: 3個用空格隔開的整數:N,X_t,Y_t
第2..N+1行: 第i+1描述了齒輪i的位置及半徑:X_i,Y_i,以及R_i
輸入樣例 (baler.in):
4 32 54
0 0 10
0 30 20
32 54 20
-40 30 20
輸入說明:
機器裏一共有4個齒輪,位於0,0的是半徑爲10的驅動齒輪,它帶動了位於0,30的,半徑爲20的某個齒輪。這個齒輪又間接帶動了位於32,54,半徑爲20的工作齒輪,以及一個位於-40,30,半徑同樣爲20的冗餘的齒輪。
輸出格式:
第1行: 輸出所有在傳動中起到作用的齒輪轉速的絕對值,包括驅動齒輪和工作齒輪。只需要輸出的整數部分,與答案相差不超過1即可。
輸出樣例 (baler.out):
20000
輸出說明:
齒輪 位置 半徑 轉速
1 (0,0) 10 10,000
2 (0,30) 20 -5,000
3 (32,54) 20 5,000
------
齒輪轉速絕對值之和:20,000


【分析】
這是一顆樹,對於樹,不需要求最短路,只需要一邊廣度優先搜索即可。

注意:1) 中心位於(0,0)的輪子不一定是第一個
          2)程序中多數變量是double 型
          3)輪子轉速的計算
          4)兩圓相切的公式就是勾股定理的平方和公式

【我的代碼】
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;

class ROLLER
{
public:
    int x;
    int y;
    int r;
    double s;
}Roller[1051];
int N;
int X,Y;
int ENum;
int SNum;

long long squre(int a)
{
    long long tmp=a*a;
    return tmp;
}

void init()
{
    scanf("%d %d %d\n",&N,&X,&Y);
    for (int i=1;i<=N;i++)
    {
        scanf("%d %d %d\n",&Roller[i].x,&Roller[i].y,&Roller[i].r);
   
        if(Roller[i].x==0 && Roller[i].y==0)
            SNum=i;
       
        if(Roller[i].x==X && Roller[i].y==Y)
            ENum=i;
    }
    Roller[SNum].s=10000;
}


class QUEUE
{
public:
    double S;//當前輪子轉速
    int C;//當前輪子的編號
    bool Rol[1051];
    QUEUE()
    {
        for (int i=1;i<=1050;i++)
            Rol[i]=true;
    }
}Q[1200];


double GetSpeed(double ss,int rd,int rx)
{
    double rrd=(double)(rd);
    double rrx=(double)(rx);
    double res=-ss*rrd/rrx;
    return res;
}

double absd(double aa)
{
    if(aa>=0)
        return aa;
    else
        return -aa;
}
   

int bfs()
{
    int Big=1;
    int Small=0;
    Q[0].Rol[SNum]=false;
    Q[0].C=SNum;
    Q[0].S=10000;
   
    int Tn,Tx,Ty,Tr;
    double Ts;
    int Nx,Ny,Nr;
    long long ta,tb,tc;
    while(Small<=Big)
    {
        Tn=Q[Small].C;
        Tx=Roller[Tn].x;
        Ty=Roller[Tn].y;
        Tr=Roller[Tn].r;
        Ts=Q[Small].S;
       
        for (int i=1;i<=N;i++)
        {
            if(i==Tn)
                continue;
           
            if(!Q[Small].Rol[i])
                continue;
           
            Nx=Roller[i].x;
            Ny=Roller[i].y;
            Nr=Roller[i].r;
           
            ta=squre(Nx-Tx);
            tb=squre(Ny-Ty);
            tc=squre(Nr+Tr);
           
            if( ta+tb!=tc )
                continue;
           
            for (int k=1;k<=N;k++)
                Q[Big].Rol[k]=Q[Small].Rol[k];
            Q[Big].C=i;
            Q[Big].S=GetSpeed(Ts,Tr,Nr);
            Q[Big].Rol[i]=false;
            Roller[i].s=Q[Big].S;
            if(i==ENum)
                return Big;
           
            Big++;
        }
       
        Small++;
    }
    return Big;
}

int main()
{
    freopen("baler.in","r",stdin);
    freopen("baler.out","w",stdout);
    init();
    int EQ=bfs();
    double Tot=0;
    for (int i=1;i<=N;i++)
    {
        if(!Q[EQ].Rol[i])
            Tot=Tot+absd(Roller[i].s);
    }
    cout<<(int)(Tot)<<endl;
    return 0;
}

[動態規劃]USACO Feb08 麻煩的聚餐 egroup 解題報告

【題目描述】
爲了避免餐廳過分擁擠,FJ要求奶牛們分3批就餐。每天晚飯前,奶牛們都會在餐廳前排隊入內,按FJ的設想,所有第3批就餐的奶牛排在隊尾,隊伍的 前端由設定爲第1批就餐的奶牛佔據,中間的位置就歸第2批就餐的奶牛了。由於奶牛們不理解FJ的安排,晚飯前的排隊成了一個大麻煩。
第i頭奶牛有一張標明她用餐批次D_i(1 <= D_i <= 3)的卡片。雖然所有N(1 <= N <= 30,000)頭奶牛排成了很整齊的隊伍,但誰都看得出來,卡片上的號碼是完全雜亂無章的。
在若干次混亂的重新排隊後,FJ找到了一種簡單些的方法:奶牛們不動,他沿着隊伍從頭到尾走一遍,把那些他認爲排錯隊的奶牛卡片上的編號改 掉,最終得到一個他想要的每個組中的奶牛都站在一起的隊列,例如111222333或者333222111。哦,你也發現了,FJ不反對一條前後顛倒的隊 列,那樣他可以讓所有奶牛向後轉,然後按正常順序進入餐廳。
你也曉得,FJ是個很懶的人。他想知道,如果他想達到目的,那麼他最少得改多少頭奶牛卡片上的編號。所有奶牛在FJ改卡片編號的時候,都不會挪位置。
程序名: egroup
輸入格式:
第1行: 1個整數:N
第2..N+1行: 第i+1行是1個整數,爲第i頭奶牛的用餐批次D_i
輸入樣例 (egroup.in):
5
1
3
2
1
1
輸入說明:
隊列中共有5頭奶牛,第1頭以及最後2頭奶牛被設定爲第一批用餐,第2頭奶牛的預設是第三批用餐,第3頭則爲第二批用餐。
輸出格式:
第1行: 輸出1個整數,爲FJ最少要改幾頭奶牛卡片上的編號,才能讓編號變成他設想中的樣子
輸出樣例 (egroup.out):
1
輸出說明:
如果FJ想把當前隊列改成一個不下降序列,他至少要改2頭奶牛的編號,一種可行的方案是:把隊伍中2頭編號不是1的奶牛的編號都改成1。不過,如果FJ選擇把第1頭奶牛的編號改成3就能把奶牛們的隊伍改造成一個合法的不上升序列了。


【分析】
題目上說的已經很清楚了,這就是一個求最長不下降/不上升序列問題。
由於N的最大值是30000,常規O(n^2)的動態規劃解法已經不能再用了,因為會有一組數據超時的。
由於這個序列中只有3個數,我們可以這樣寫:
定義 Q[i]表示:當前以i結尾的最長不下降序列的長度(1<=i<=3)
定義 H[i]表示:當前以i結尾的最長不上升序列的長度(1<=i<=3)
兩個數組都初始化為0。

現在連數組都不用開,直接讀入,邊讀邊算即可。
定義Tmp表示當前讀入的數。
狀態轉移方程:
                        如果Tmp=1:Q[1]=Q[1]+1,H[Tmp]=Max{H[1],H[2],H[3]}+1;
                        如果Tmp=2:Q[2]=Max{Q[1],Q[2]}+1,H[2]=Max{H[2],H[3]}+1;
                       如果Tmp=3:Q[3]=Max{Q[1],Q[2],Q[3]}+1,H[3]=H[3]+1;
最後輸出Min{Max{Q[1],Q[2],Q[3]},Max{H[1],H[2],H[3]}}即可。



【代碼一:O(n^2)的動態規劃】
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int H[30001];
int S[30001];
int X[30001];
int n;

void init()
{
    scanf("%d\n",&n);
    for (int i=1;i<=n;i++)
        scanf("%d\n",&H[i]);
}

void dp()
{
    //int maxn=0;
    for (int k=1;k<=n;k++)
    {
        //先DP出最長上升序列
        S[1]=1;
        for (int i=2;i<=n;i++)
        {
            S[i]=1;
            for (int j=1;j<=i-1;j++)
            {
                if(H[i]>=H[j] && S[j]+1>S[i])
                    S[i]=S[j]+1;
            }
        }
       
        //再求最長下降序列
        X[n]=1;
        for (int i=n-1;i>=1;i--)
        {
            X[i]=1;
            for (int j=i+1;j<=n;j++)
            {
                if(H[i]>=H[j] && X[j]+1>X[i])
                    X[i]=X[j]+1;
            }
        }
    }
   
   
    int Maxa=0;
    int Maxb=0;
    for (int i=1;i<=n;i++)
    {
        if(S[i]>Maxa)
            Maxa=S[i];
        if(X[i]>Maxb)
            Maxb=X[i];
    }
    int Min=n-Maxa;
   
    if(Min>(n-Maxb))
        Min=n-Maxb;
   
    cout<<Min<<endl;
}

int main()
{
    freopen("egroup.in","r",stdin);
    freopen("egroup.out","w",stdout);
    init();
    dp();
    //printf("%d\n",n-dp());
    return 0;
}



【代碼二:O(n)的動態規劃】
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

int Max2(int a,int b)
{
    if(b>a)
        a=b;
    return a;
}

int Max3(int a,int b,int c)
{
    if(b>a)
        a=b;
    if(c>a)
        a=c;
    return a;
}

void work()
{
    int Q[4]={0,0,0,0};
    int H[4]={0,0,0,0};
    int N;
    int Tmp=0;

    scanf("%d\n",&N);
   
    for (int i=1;i<=N;i++)
    {
        cin>>Tmp;
        if(Tmp==1)
        {
            Q[1]++;
            H[1]=Max3(H[1],H[2],H[3])+1;
            continue;
        }
        if(Tmp==2)
        {
            Q[2]=Max2(Q[1],Q[2])+1;
            H[2]=Max2(H[2],H[3])+1;
            continue;
        }
        Q[3]=Max3(Q[1],Q[2],Q[3])+1;
        H[3]++;
    }
    int MAX1=Max3(H[1],H[2],H[3]);
    int MAX2=Max3(Q[1],Q[2],Q[3]);
    MAX1=N-Max2(MAX1,MAX2);
    printf("%d\n",MAX1);
}

int main()
{
    freopen("egroup.in","r",stdin);
    freopen("egroup.out","w",stdout);
    work();
    return 0;
}

NOI1998 個人所得稅 personaltax 解題報告

【題目描述】
某國個人所得稅法規定,普通公民的主要應納稅收入項目及納稅金額如下:
工資、薪金所得。按月計算徵稅,以每月收入額減除費用800元后的餘額作爲該月應納稅所得額,稅率如下表所示:
級數 月應納稅所得額 稅率(%)
1 不超過500元的 5
2 超過500元~2000元的部分 10
3 超過2000元~5000元的部分 15
4 超過5000元~20000元的部分 20
5 超過20000元~40000元的部分 25
6 超過40000元~60000元的部分 30
7 超過60000元~80000元的部分 35
8 超過80000元~100000元的部分 40
9 超過100000元的部分 45


一次性勞動報酬所得。按次計算徵稅,每次不超過4000元的,減除費用800元;4000元以上的,減除20%的費用,餘額爲應納稅所得額。徵稅稅率如下表所示:
級數 每次應納稅所得額 稅率(%)
1 不超過20000元的部分 20
2 超過20000元~50000元的部分 30
3 超過50000元的部分 40
由上面可以看出,個人工資、薪金及一次性勞動報酬所得都是按照超額累進稅率來徵稅的。超額累進稅率將應納稅所得額按數額大小分成若乾等級,每一等級 規定一個稅率,稅率依次提高,但每一納稅人的的應納稅所得額依照所屬等級同時適用幾個稅率分別計算,將計算結果相加後的總額作爲應納稅款。
例如,某人某月工資總額爲3800元,減去800元后,應納稅所得額爲3000元。其中1級500元,2級1500元,3級1000元, 稅率分別爲5%、10%、15%,應納稅總額爲500*5%+1500*10%+1000*15%=325(元)。現在需要你編一程序,根據該國某公司的 所有職員一年內的各項收入信息(收入項目、收入時間、收入金額)計算該公司所有職員這一年應交納的個人所得稅總額。
輸入
輸入文件的第一行爲一個正整數M(M<= 50000),表示該公司的職員總數(職員編號依次爲1,2,…,M)。接下來的各行每行表示一年內某一個職員的一項收入信息。具體格式如下: 工資、薪金收入信息:PAY 職員編號 收入時間 收入金額
一次性勞務報酬收入信息:INCOME 職員編號 收入時間 收入金額
其中,收入時間格式爲:MM/DD,MM表示月份(1<= MM<=12),DD表示日期(1<= DD<=31);收入金額是一個正整數(單位:元),並假設每人每項收入金額小於100萬元。
輸入文件以字符“#”表示結束。輸入文件中同一行相鄰兩項之間用一個或多個空格隔開。
輸出
輸出文件只有一個正數P(保留兩位小數),P表示該公司所有職員一年內應交納的個人所得稅總額(單位:元)。
樣例輸入
2
PAY 1 2/23 3800
INCOME 2 4/8 4010
INCOME 2 4/18 800
PAY 1 8/14 6700
PAY 1 8/10 1200
PAY 2 12/10 20000
#
樣例輸出
5476.60

【分析】
又是一道NOI的水題啊~這道題不需要什麼算法或者數據結構,模擬即可。
讀題時應注意:工資、薪金所得是按月計算,一次性勞動報酬所得是按此次計算,所以說,輸入數據中給的日期對於一次性勞動報酬所得的計算沒有任何作用,而對於工資、薪金所得是按月來說,只有月份有用而日無用。所以讀入時應忽略干擾數字。用C++/C中的scanf()函數很容易做到這一點。

剩下的就很簡單了,就是初中時學習的分段函數,只要看清題目就不會出錯。

【代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int M;

class PAY
{
public:
    int Mon[13];
    double TP;
    double TI;
    PAY()
    {
        for (int i=1;i<=12;i++)
            Mon[i]=0;
        TP=0;
        TI=0;
    }
}PE[50001];


double Jianzhi(int Num)
{
    double MM=0;
    double NN=Num;
    if(Num<=800)
        return MM;
   
    if(Num<=4000)
        NN=NN-800;
    else
    {
        NN=NN-NN/5;
    }
   
    if(NN<=20000)
    {
        MM=NN/5;
        return MM;
    }
   
    if(NN<=50000)
    {
        MM=4000;
        NN=NN-20000;
        MM=MM+NN*0.3;
        return MM;
    }
   
    MM=4000+9000;
    NN=NN-50000;
    MM=MM+NN*0.4;
    return MM;
}


double Gongzi(int Num)
{
    double NN=Num;
    double MM=0;
    if(Num<=800)
    {
        return MM;
    }
   
    NN=NN-800;
   
    if(NN<=500)
    {
        MM=NN/20;
        return MM;
    }
   
    if(NN<=2000)
    {
        MM=25;
        NN=NN-500;
        MM=MM+NN*0.1;
        return MM;
    }
   
    if(NN<=5000)
    {
        MM=175;
        NN=NN-2000;
        MM=MM+NN*0.15;
        return MM;
    }
   
    if(NN<=20000)
    {
        MM=625;
        NN-=5000;
        MM=MM+NN*0.2;
        return MM;
    }
   
    if(NN<=40000)
    {
        MM=3625;
        NN-=20000;
        MM=MM+NN*0.25;
        return MM;
    }
   
    if(NN<=60000)
    {
        MM=8625;
        NN-=40000;
        MM=MM+NN*0.3;
        return MM;
    }
   
    if(NN<=80000)
    {
        MM=14625;
        NN-=60000;
        MM=MM+NN*0.35;
        return MM;
    }
   
    if(NN<=100000)
    {
        MM=21625;
        NN-=80000;
        MM=MM+NN*0.4;
        return MM;
    }
   
    MM=29625;
    NN-=100000;
    MM=MM+NN*0.45;
    return MM;
}

void init()
{
    string PAY="PAY";
    string INCOME="INCOME";
    scanf("%d\n",&M);
    string P;
    P.clear();
    cin>>P;
    int Peo;
    int Mon;
    int Day;
    int Money;
   
    while(P[0]!='#')
    {
        if(P==PAY)
        {
            scanf("%d %d/%d %d",&Peo,&Mon,&Day,&Money);
            PE[Peo].Mon[Mon]+=Money;
        }
        if(P==INCOME)
        {
            scanf("%d %d/%d %d",&Peo,&Mon,&Day,&Money);
            PE[Peo].TI=PE[Peo].TI+Jianzhi(Money);
        }
        cin>>P;
    }
   
    for (int i=1;i<=M;i++)
    {
        for (int j=1;j<=12;j++)
        {
            if(PE[i].Mon[j]!=0)
                PE[i].TP+=Gongzi(PE[i].Mon[j]);
        }
    }
   
    double Tot=0;
    for (int i=1;i<=M;i++)
    {
        Tot+=PE[i].TP;
        Tot+=PE[i].TI;
    }
    //cout<<Tot<<endl;
    printf("%.2lf\n",Tot);
}

int main()
{
    freopen("personaltax.in","r",stdin);
    freopen("personaltax.out","w",stdout);
    init();
    return 0;
}

USACO月賽 Oct08 牧場旅行 pwalk

【問題描述】
n個被自然地編號爲1..n奶牛(1<=n<=1000)正在同樣被方便的編號爲1..n的n個牧場中吃草。更加自然而方便的是,第i個奶牛就在第i個牧場中吃草。
其中的一些對牧場被總共的n-1條雙向通道的一條連接。奶牛可以通過通道。第i條通道連接的兩個牧場是A_i和B_i(1<=A_i<=N;1<=B_i<=N)其長度是L_i(1<=L_i<=10000)。
通道只會連接兩個不同的牧場,所以這些通道使得整個牧場構成了一棵樹。
奶牛們是好交際的希望能夠經常的訪問別的奶牛。急切地,它們希望你能通過告訴它們Q(1<=Q<=1000)對牧場的路徑來幫助他們安排旅行。(這裏將有Q個詢問,p1,p2(1<=p1<=n;1<=p1<=n))
分數:200
問題名稱:pwalk
輸入格式:
第1行:兩個用空格隔開的整數:n和Q
第2..n行:第i+1行包含三個用空格隔開的整數:A_i,b_i和c_i
第n+1..N+Q行:每行包含兩個用空格隔開的整數,代表兩個不同的牧場,p1和p2
輸入樣例(file pwalk.in):
4 2
2 1 2
4 3 2
1 4 3
1 2
3 2
輸出格式:
第1..Q行:行i包含第i個詢問的答案。
輸出樣例:
2
7
輸出說明:
詢問1:牧場1和牧場2的路徑長度爲2。 詢問2:3->4->1->2;總長爲7。

【分析】
這明明是一棵樹,直接廣搜即可過全數據,沒必要求最短路。用Floyd還會超時。

【代碼一:鄰接矩陣】
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int N;
int QQ;
unsigned int Mat[1001][1001];

void print()
{
    for (int i=1;i<=N;i++)
    {
        for (int j=1;j<=N;j++)
            cout<<Mat[i][j]<<" ";
        cout<<endl;
    }
    cout<<endl;
}

unsigned int Q[1002];
unsigned int K[1002];
bool used[1001];

unsigned  int bfs(int S,int E)
{
    for (int i=1;i<=N;i++)
        used[i]=true;
    used[S]=false;
   
    int rear=1;
    int head=0;
    Q[0]=S;
    K[0]=0;
    int T;
    while(head<=rear)
    {
        T=Q[head];
        if(T==E)
            return K[head];
        for (int i=1;i<=N;i++)
        {
            if(Mat[T][i]>0 && used[i])
            {
                Q[rear]=i;
                K[rear]=K[head]+Mat[T][i];
                //Mat[S][i]=K[rear];
                //Mat[i][S]=K[rear];
                used[i]=false;
                rear++;
            }
        }
        head++;
    }
    return K[rear];
}

void init()
{
    scanf("%d %d\n",&N,&QQ);
   
   
    int a,b,c;
    for (int i=1;i<=N-1;i++)
    {
        scanf("%d %d %d\n",&a,&b,&c);
        Mat[a][b]=c;
        Mat[b][a]=c;
    }
   
    //print();
   
    for(int i=1;i<=QQ;i++)
    {
        scanf("%d %d\n",&a,&b);
        printf("%d\n",bfs(a,b) );
    }
    return;
}



int main()
{
    freopen("pwalk.in","r",stdin);
    freopen("pwalk.out","w",stdout);
   
    init();
   
    return 0;
}



【代碼二:鄰接表】
#include <cstdio>
using namespace std;

int way[1001]={0},map[1001][1000]={{0}},dis[1001][1000]={{0}};

void bfs(int st,int en)
{
    int i,que[1000]={0},len[1000]={0},tail=0,head=0;
    bool used[1001]={false};
    que[0]=st;
    len[0]=0;
    used[st]=true;
    while (tail<=head)
    {
        for (i=0;i<way[que[tail]];i++)
            if (!used[map[que[tail]][i]])
            {
                head++;
                que[head]=map[que[tail]][i];
                len[head]=len[tail]+dis[que[tail]][i];
                used[que[head]]=true;
                if (que[head]==en)
                {
                    printf("%d\n",len[head]);
                    return;
                }
            }
        tail++;
    }
}

int main(void)
{
    freopen("pwalk.in","r",stdin);
    freopen("pwalk.out","w",stdout);
    int i,n,q,temp,temp2,temp3;
    scanf("%d %d\n",&n,&q);
    for (i=1;i<n;i++)
    {
        scanf("%d %d %d\n",&temp,&temp2,&temp3);
        map[temp][way[temp]]=temp2;
        map[temp2][way[temp2]]=temp;
        dis[temp][way[temp]]=temp3;
        dis[temp2][way[temp2]]=temp3;
        way[temp]++;
        way[temp2]++;
    }
    for (i=1;i<=q;i++)
    {
        scanf("%d %d\n",&temp,&temp2);
        bfs(temp,temp2);
    }
    fclose(stdin);
    fclose(stdout);
    return (0);
}

NOI1997 競賽排名 competitionsort 解題報告

【題目描述】
題目網上都有,比如說http://www.byvoid.com/blog/noi-1997-solution/ 。

【分析】
這是道NOI中的水題,簡單的模擬即可,只需要看清楚題目中給的糾結的式子即可。最後需要結構體的三級排序。

【代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

class Student
{
public:
    int G[9];
    double Sum;
    double Pos[9];
    double SumP;
    int n;
}S[1001];
int N;
double Av[8];


double Abs(double n)
{
    if(n>0)
        return n;
    return -n;
}

void Averge(int I)
{
    int Tot=0;
    for (int i=1;i<=N;i++)
        Tot+=S[i].G[I];
    double TT=Tot;
    double NN=N;
    Av[I]=TT/NN;
}

bool flag=true;

void print()
{
    for (int i=1;i<=N;i++)
    {
        for (int j=1;j<=8;j++)
            cout<<S[i].G[j]<<" ";
        cout<<" "<<S[i].Sum<<" "<<S[i].SumP<<" "<<S[i].n<<endl;
    }
    cout<<endl;
    for (int i=1;i<=8;i++)
        cout<<Av[i]<<" ";
   }

void Position(int I)
{
    for (int k=1;k<=8;k++)
    {
        double E=0;
        for (int i=1;i<=N;i++)
        {
            double Tmp;
            Tmp=S[i].G[k];
            E+=( Abs(Tmp-Av[k])  );
        }
       
        if(E==0)
        {
            S[I].Pos[k]=0;
            continue;
        }
       
        /*計算Xij-AVGj*/
        double Xij;
        double Up;
        double Down;
        Xij=S[I].G[k];
        Up=Xij-Av[k];
        Down=E/N;
        S[I].Pos[k]=Up/Down;
    }
   
    double Sp=0;
    for (int i=1;i<=3;i++)
        Sp+=S[I].Pos[i];
   
    for (int i=4;i<=8;i++)
        Sp+=(S[I].Pos[i]*0.8);
    S[I].SumP=Sp;
    return;
}

int cmp(const void *a,const void *b)
{
    class Student *c=(class Student *)a;
    class Student *d=(class Student *)b;
    if(c->SumP!=d->SumP)
    {
        if(d->SumP>=c->SumP)
            return 1;
        else
            return -1;
    }
    else
    {
        if(c->Sum!=d->Sum)
            return d->Sum>c->Sum?1:-1;
        else
            return c->n-d->n;
    }
}

  
void init()
{
    scanf("%d\n",&N);
    
    for (int i=1;i<=N;i++)
    {
        int Tmp=0;
        for(int j=1;j<=8;j++)
        {
            cin>>S[i].G[j];
            Tmp+=S[i].G[j];
        }
        S[i].Sum=Tmp;
        S[i].n=i;
    }
   
    for (int i=1;i<=8;i++)
        Averge(i);
    for (int i=1;i<=N;i++)
        Position(i);
    qsort(S+1,N,sizeof(S[0]),cmp);
    return;
}


int main()
{
    freopen("competitionsort.in","r",stdin);
    freopen("competitionsort.out","w",stdout);
    init();
    //print();
    if(!flag)
        return 0;
    for (int i=1;i<=N;i++)
        cout<<S[i].n<<endl;
    return 0;
}

2011年11月2日 星期三

[貪心策略]USACO Mar05 yogfac 奶酪工廠 解題報告

      USACO Mar05 yogfac 奶酪工廠 解題報告
 【問題描述】
奶牛們開辦了一個奶酪工廠來生產世界著名的約克奶酪。在接下來的 N (1<=N<=10000) 星期中,由於牛奶和勞動力的價格變化,奶酪的成本也在變化。因此奶酪工廠在第 i 個星期要花 C_i (1<=C_i<=5000) 分來生產一個單位的奶酪。 由於採用了最先進的技術,約克奶酪工廠在一個星期內可以生產任意多的奶酪。
約克奶酪工廠擁有一個無限大的倉庫, 每個星期生產的多餘的奶酪都會放在這裏。而且每個星期存放一個單位的奶酪要花費 S (1<=S<=100) 分,不過幸運的是由於採用了最先進的技術,奶酪在倉庫裏不會壞 ^_^
工廠最近收到了客戶 N 個星期的訂單,第 i 個星期要向客戶提供 Y_i (0<=Y_i<=10000) 個單位的奶酪。當然這些奶酪可以在第 i 個星期時生產,也可以從倉庫中拿取。
採用怎麼樣的生產策略纔會使約克工廠的花費最小呢?你能幫幫它們嗎?
【輸入格式】
* 第 1 行兩個整數: N 和 S
* 接下來的 N 行中,第 i 行的兩個數表示: C_i 和 Y_i
【輸出格式】
* 輸出僅一行,工廠生產的最小花費
【輸入輸出樣例】
factory.in
4 5
88 200
89 400
97 300
91 500

factory.out
126900
【輸入輸出樣例說明】
最佳生產方案如下:第一個星期生產 200 個單位的奶酪全部送給客戶,第二個星期生產 700 個單位的奶酪, 400 個送給客戶,另外 300 個放在倉庫。第三個星期把倉庫中的 300 個奶酪賣給客戶,第四個星期生產 500 個單位的奶酪賣給客戶。

【分析】
貪心策略,要算出某個星期最小的花費,只需要枚舉 從第一個星期 到 這個星期的生產+儲存的最小值,然後輸出總的花費即可。

【代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int N;
int S;
long long Tot=0;

class Factory
{
public:
    long long Cost;
    long long Need;
}Fac[10002];

void init()
{
    cin>>N>>S;
    for (int i=1;i<=N;i++)
        cin>>Fac[i].Cost>>Fac[i].Need;
    return;
}

void work()
{
    for (int i=1;i<=N;i++)
    {
        long long tmp=Fac[i].Cost*Fac[i].Need;
        for (int j=1;j<=i-1;j++)
        {
            if(tmp> ( Fac[j].Cost * Fac[i].Need +(i-j)*S*Fac[i].Need) )
                tmp=Fac[j].Cost * Fac[i].Need +(i-j)*S*Fac[i].Need;
        }
        Tot+=tmp;
    }
    cout<<Tot<<endl;
}

int main()
{
    freopen("factory.in","r",stdin);
    freopen("factory.out","w",stdout);
    init();
    work();
    return 0;
}

[字符串處理][模擬]OI練習題:IP網絡管理員 networkip

【問題描述】
Alex 是一個 IP 網絡管理員。他的顧客擁有一堆私人 IP 地址,他想把這些 IP 地址組成一個最小的 IP 網絡。

每個 IP 地址是由 4 個 byte 類型數順次由 3 個 dot 連接而成,形如 'byte0.byte1.byte2.byte 3' (不計引號)。每個 byte 類型數是一個 0 至 255 (包括 0 和 255 )的首位不爲零的十進制整數。

IP 網絡由網絡地址和網絡掩碼來描述,他們的描述方式與 IP 地址相同。爲了準確的理解 IP 地址、網絡地址和網絡掩碼的意義,你需要把它們按照二進制表示寫出。他們的二進制表示都由 32 bits 組成: 8 bits 描述 byte0 、然後 8 bits 描述 byte1 、然後 8 bits 描述 byte2 、最後 8 bits 描述 byte3 。

特定的 IP 網絡包含 2^n 個 IP 地址。它的網絡掩碼的前 32-n 個 bits 爲 1 ,後 n 個 bits 爲 0 ;其網絡地址的前 32–n 個 bits 爲 0 或者 1 ,後 n 個 bits 爲 0 。這個 IP 網絡包含了所有前 32–n 個 bits 與其網絡地址相同且後 n 個 bits 任意的所有 IP 地址,總共 2^n 個。我們說一個 IP 網絡比另一個 IP 網絡小,當且僅當它包含更少的 IP 地址。
比如,網絡地址和網絡掩碼分別爲 194.85.160.176 和 255.255.255.248 的 IP 網 絡包含了從 194.85.160.176 至 194.85.160.183 的 IP 地址。
【輸入格式】
第一行一個正整數 m ( 1 <= m <= 1000 )表示 Alex 的 IP 地址數。然後 m 行每行描述一個 IP 地址。
【輸出格式】
兩行,分別表示能夠包含所有 IP 地址的最小 IP 網絡的網絡地址和網絡掩碼。
【輸入輸出樣例】
networkip.in
3
194.85.160.177
194.85.160.183
194.85.160.178

networkip.out
194.85.160.176
255.255.255.248

【分析】
這題看著很難,其實很簡單,就是一個簡單的字符串處理問題。
首先是IP地址的讀入,用scanf讀入會很方便的。
然後統計這N個IP地址二進制表示 從前面開始的 最長的公共部分,記為S,即這N個IP地址二進制表示 從前面數S個數都是相同的。

根據題意:把子網掩碼的 前S位置為1,後32-S位為0。
 把IP地址的後32-S位置為0,然後再轉換為10進制輸出即可。

例如樣例:
這三個IP地址的二進制表示為:
194.85.160.177   11000010 01010101 10100000 10110001 194.85.160.183   11000010 01010101 10100000 10110111 194.85.160.178   11000010 01010101 10100000 10110010 
可以看出,這三個IP地址的二進制表示有29位是相同的。


根據題意,把子網掩碼的 前S位置為1,後32-S位為0:
Subnet Mark:11111111 11111111 11111111 11111000 


把IP地址的後32-S位置為0,即為最小IP地址:
 IP Adress:11000010 01010101 10100000 10110000

然後把Subnet Mark和IP Address轉換為10進制輸出即可。

 11111111 11111111 11111111 11111000 =>255.255.255.248


11000010 01010101 10100000 10110000 =>194.85.160.176
【我的代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

class IPAddress
{
public:
    int b[5];
    char B[5][10];
    IPAddress()
    {
        for (int i=1;i<=4;i++)
            memset(B[i],'\0',sizeof(B[i]));
    }
}IP[1001];

int N;
int Same=0;
char Sam[5][10];

int power(int base,int index)
{
    int l=1;
    for (int i=1;i<=index;i++)
        l*=base;
    return l;
}

void num2str(int num,char *s,int base)
{
    char map[] = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    char dst[64];
    int i=0,n;
    while(num)
    {
        dst[i++]=map[num%base];
        num/=base;
    }
    dst[i]='\0';
    n=i;
    for(i=n-1;i>=0;--i)
    {
        s[n-1-i]=dst[i];
    }
    s[n]='\0';
}

void init()
{
    for (int i=1;i<=4;i++)
        memset(Sam[i],'\0',sizeof(Sam[i]));
   
    scanf("%d\n",&N);
    char tmp[10];
    for (int i=1;i<=N;i++)
    {
        scanf("%d.%d.%d.%d\n",&IP[i].b[1],&IP[i].b[2],&IP[i].b[3],&IP[i].b[4]);
        for (int j=1;j<=4;j++)
        {
            memset(tmp,'\0',sizeof(tmp));
            num2str(IP[i].b[j],tmp,2);
            int len=strlen(tmp);
            int top=0;
            for (int k=0;k<8-len;k++)
            {
                IP[i].B[j][k]='0';
                top++;
            }
            for (int k=0;k<len;k++)
            {
                IP[i].B[j][top]=tmp[k];
                top++;
            }
        }
    }
}

void work()
{
    for (int k=1;k<=4;k++)
    {  
        for (int i=0;i<8;i++)
        {
            //bool ok=true;
            //int tmp=IP[1].B[k][i];
            for (int j=2;j<=N;j++)
            {
                if(IP[j].B[k][i]!=IP[1].B[k][i])
                    return;
            }
            Sam[k][i]=IP[1].B[k][i];
            Same++;
        }
    }
}


void computer()
{
    /*Subnet Mask*/
    char SM[5][10];
    for (int i=1;i<=4;i++)
    {
        for (int j=0;j<8;j++)
        {
            SM[i][j]=Sam[i][j];
            if(SM[i][j]!='\0')
                SM[i][j]='1';
            else
                SM[i][j]='0';
        }
    }
   
    /*IP Address*/
    char AD[5][10];
    for (int i=1;i<=4;i++)
    {
        for (int j=0;j<8;j++)
        {
            AD[i][j]=Sam[i][j];
            if(AD[i][j]=='\0')
                AD[i][j]='0';
        }
    }
   
 
   
    for (int i=1;i<=4;i++)
    {
        int T=0;
        for (int j=0;j<8;j++)
        {
            T=T+(AD[i][j]-'0')*power(2,7-j);
        }
      
        if(i<4)
            cout<<T<<".";
        else
            cout<<T<<endl;
    }
   
   
    for (int i=1;i<=4;i++)
    {
        int T=0;
        for (int j=0;j<8;j++)
        {
            T=T+(SM[i][j]-'0')*power(2,7-j);
        }
            if(i<4)
            cout<<T<<".";
        else
            cout<<T<<endl;
    }
   
}

int main()
{
    freopen("networkip.in","r",stdin);
    freopen("networkip.out","w",stdout);
    init();
   
    work();

    computer();
   
    return 0;
}



【評測結果】
正在连接评测机...

已连接到评测机
GRID 1
名称 Flitty
系统版本 1.00
备注 COGS 1号评测机 Flitty
正在编译...
编译成功

测试点 结果 得分 运行时间 内存使用 退出代码
1 正确 10 0.000 s 347 KB 0
2 正确 10 0.000 s 347 KB 0
3 正确 10 0.000 s 347 KB 0
4 正确 10 0.000 s 347 KB 0
5 正确 10 0.001 s 347 KB 0
6 正确 10 0.002 s 347 KB 0
7 正确 10 0.002 s 347 KB 0
8 正确 10 0.002 s 347 KB 0
9 正确 10 0.002 s 347 KB 0
10 正确 10 0.000 s 347 KB 0
运行完成
运行时间 0.010 s
平均内存使用 347 KB
测试点通过状况 AAAAAAAAAA
得分:100
恭喜你通过了全部测试点!