申請SAE

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

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

2011年12月26日 星期一

[並查集]HAOI 破譯密文 encrypt 解題報告

【題目描述】  查看題目
原文鏈接:http://yeefan.tk/blog/haoi-encrypt/
資訊的明文是由0利1組成的非空序列。但在網絡通信中,為了資訊的安全性,常對明文進行加密,用密文進行傳輸。密文是由0、1和若干個密碼字母組成,每個密碼字母代表不同的01串,例如,密文二011a0bf00a01。密碼破譯的關鍵是確定每個密碼的含義。
經過長期統計分析,現在知道了每個密碼的固定長度,如今,我方又截獲了敵方的兩段密文S1和S2,並且知道S1二S2,即兩段密文代表相同的明文。你的任務是幫助情報人員對給定的兩段密文進行分析,看一看有多少種可能的明文。
【輸入文件】
第1行: S1 (第1段密文)
第2行: S2 (第2段密文)
第3行: N (密碼總數, N<=26)
第4—N+3行: 字母i 長度i (密碼用小寫英文字母表示, 密碼長度<=100)

【輸出文件】
M(表示有M種可能的明文)

【輸入輸出樣例】
encrypt.in
100ad1
cc1
4
a 2
d 3
c 4
b 50
encrypt.out
2
【約束條件】
明文的長度<=10000

【分析】
既然每個密文代表一段明文,那我們可以把密文展開
如樣例的:
100ad1
cc1
4
a 2
d 3
c 4
b 50
我們就可以展開成
1 0 0 a1 a2 d1 d2 d3 1
c1 c2 c3 c4 c1 c2 c3 c4 1
那麼問題就成了在這麼一串數中找出有多少個數不定集合.
即c1代表1 c2代表0 c3代表0
但是 c4和a1共同代表什麼呢,這是問題的關鍵,這就用到了並查集:
首先 1 0 a1 a2…d2 d3都是獨立的集合
從i=1開始向右掃描
先把1所在的集合和c1所在的集合歸成一類,
0所在的集合和c2所在的集合歸成一類
….
最後一定是
代表0的字母有一個集合
代表1的字母有一個集合
不能確定代表什麼的有x個集合
因為每個集合代表的數字要麼是1要麼是0有兩種情況
所以兩個集合有4種情況
三個集合有8種情況
x個集合有2^x種情況
所以我們只需要用並查集算出有幾個非0或1的集合,再累乘後輸出即可
注意:有一個密文既對應0也對應1,這種情況構不成任何正確的明文,所以應當輸出0。

【我的代碼】
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
int num[30];
 
const char base='a'-1;
const int MAXN=3000;
int Letter[30];//每個密碼的密碼長度
int Num[30][102];
char S1[105];
char S2[105];
int N;//密碼總數
int T=0;//不定集合的數量
int Sum=0;//最終結果
int M=0;
int DealA[MAXN]={0};
int DealB[MAXN]={0};
int Num0;
int Num1;
int Len=0;
 
class Node
{
public:
 int parent;
 int count;
}UFS[MAXN];
 
void UFS_Init(int i)
{
 UFS[i].count=1;
 UFS[i].parent=i;
}
 
int UFS_Find(int x)
{
 int i=x;
 while(i!=UFS[i].parent)
  i=UFS[i].parent;
 
 int j=x;
 while(j!=i)
 {
  int tmp=UFS[j].parent;
  UFS[j].parent=i;
  j=tmp;
 }
 return i;
}
 
void UFS_Merge(int x,int y)
{
 x=UFS_Find(x);
 y=UFS_Find(y);
 if(x==y)
  return;
 
 if(UFS[x].count>UFS[y].count)
 {
  UFS[y].parent=x;
  UFS[x].count+=UFS[y].count;
 }
 else
 {
  UFS[x].parent=y;
  UFS[y].count+=UFS[x].count;
 }
}
 
void init()
{
 scanf("%s\n%s\n",&S1,&S2);
 scanf("%d\n",&N);
 memset(Letter,0,sizeof(Letter));
 char cTmp;
 int iTmp;
 for (int i=1;i<=N;i++)
 {
  scanf("%c %d\n",&cTmp,&iTmp);
  Letter[cTmp-base]=iTmp;
 } 
 
 for(int i=1;i<=26;i++)
 {
  for (int j=1;j<=Letter[i];j++)
  {
   M++;
   Num[i][j]=M;
   UFS_Init(M);
  }
 }
 
 M++;
 Num0=M;
 UFS_Init(M);
 
 M++;
 Num1=M;
 UFS_Init(M);
 
 int top=0;
 for (unsigned int i=0;i<strlen(S1);i++)
 {
  if(isalpha(S1[i]))
  {
   for (int j=1;j<=Letter[ S1[i]-base ]; j++)
   {
    top++;
    DealA[top]=Num[S1[i]-base][j];
   }
  }
  else
  {
   if(S1[i]=='0')
   {
    top++;
    DealA[top]=Num0;
   }
   else
   {
    top++;
    DealA[top]=Num1;
   }
  }
 }
 
 Len=top;
 top=0;
 for (unsigned int i=0;i<strlen(S2);i++)
 {
  if(isalpha(S2[i]))
  {
   for (int j=1;j<=Letter[ S2[i]-base ]; j++)
   {
    top++;
    DealB[top]=Num[S2[i]-base][j];
   }
  }
  else
  {
   if(S2[i]=='0')
   {
    top++;
    DealB[top]=Num0;
   }
   else
   {
    top++;
    DealB[top]=Num1;
   }
  }
 } 
}
 
int work()
{
 for(int i=1;i<=Len;i++)
 {
  UFS_Merge(DealA[i],DealB[i]);
 }
 
 if(UFS_Find(Num0)==UFS_Find(Num1))
 {
  return 0;
 }
 
 bool Used[MAXN];
 memset(Used,0,sizeof(Used));
 
 int Find_Num0=UFS_Find(Num0);
 int Find_Num1=UFS_Find(Num1);
 for(int i=1;i<=Len;i++)
 { 
  int tmp=UFS_Find(DealA[i]);
  if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp])
   continue;
 
  T++;
  Used[tmp]=true;
 }
 
 for(int i=1;i<=Len;i++)
 {
  int tmp=UFS_Find(DealB[i]);
  if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp])
   continue;
 
  T++;
  Used[tmp]=true;
 }
 
 Sum=1;
 for (int i=1;i<=T;i++)
  Sum*=2;
 return Sum;
}
 
int main()
{
 freopen("encrypt.in","r",stdin);
 freopen("encrypt.out","w",stdout);
 init();
 printf("%d\n",work());
 return 0;
}

2011年12月19日 星期一

[最短路徑]USACO Silver09 找工作 jobhunt 解題報告

問題描述:
貝茜牛身無分文了,她正忙着找工作。農夫約翰知道這個情況,他想讓他的牛去周遊世界,於是他推行了一個規則:在他的牛到另 一個城市工作之前,她們只能在一個城市掙得 D ( 1 <= D <= 1,000 )美元。不管怎樣,貝茜可以在別的城市工作過之後,再返回到某個城市,並在這個城市再掙 D 美元,她可以無限次數地這樣做。
貝茜牛的世界包括 P ( 1 <= P <= 150 )條單向邊,這些邊連接着 C ( 2 <= C <= 220 )個城市,城市按 1 到 C 的順序編號,貝茜牛目前正待在 S 城 (1 <= S <= C) 。單向邊 i 從城市 A_i 連到城市 B_i ,其中 1 <= A_i <= C; 1 <= B_i <= C ,在路上不花費任何代價。
爲了幫助貝茜,約翰授權它使用他的私人噴氣飛機服務。這項服務配置了 F 條航綫,每條航綫是由城市 J_i 到城市 K_i (1 <=J_i <= C; 1 <= K_i <= C) 的單向航綫,且在該航綫上的費用是 T_i( 1 <= T_i <= 50,000 ) 美元,如果貝茜牛手頭沒有現錢,它可以將來掙到錢之後再支付飛行費用。
只要它願意,貝茜可以隨時隨地選擇退出。不限時間,假定它所有去過的城市都能掙足 D 美元,最後貝茜最多能得到多少錢?如果這個數目沒有限制的話輸出 -1 。
程序名:jobhunt
輸入格式:
第1行:五個空格隔開的整數,D,P,C,F,S;
第2至P+1行:第i行包括兩個空格隔開的整數,表示從城市A_i到B_i有一條單向邊。
第P+2至P+F+1行:第P+i行包括三個空格隔開的整數,表示從城市J_i到T_i有一條單向航綫,費用是T_i。
輸入樣例:(jobhunt.in):
100 3 5 2 1
1 5
2 3
1 4
5 2 150
2 5 120
輸入樣例解釋:這個世界有5個城市,三條有向邊,和兩條飛行航綫,貝茜從城市1開始,在每個城市它能最多掙到100美元。
輸出格式:
只有一行,一個整數,表示在遵守規則的情況下,它最多能得到多少錢。
輸出樣例:(jobhunt.out):
250
輸出樣例解釋:貝茜能從城市1→城市5→城市2→城市3,最後共得到4*100 - 150 = 250美元。
「分析」
單源最短路問題,賺錢是負權,航費是正權,用SPFA處理負邊權即可。
「我的代碼」
#include "cstdio"
#include "iostream"
#include "cstdlib"
#include "queue"
using namespace std;
const int MAX=230;
int Map[MAX][MAX];
int dist[MAX];
int times[MAX];
bool flag[MAX];
const int MAXN=1000000000;
int D;//在每個城市最多掙得D美金
int P;//P條單向邊
int C;//C個城市
int F;//F個單項航線
int S;//源點
typedef queue QUEUE;
void init()
{
 scanf("%d %d %d %d %d\n",&D,&P,&C,&F,&S);
 for (int i=1;i<=C;i++)
  for (int j=1;j<=C;j++)
   Map[i][j]=MAXN;
 for (int i=1;i<=C;i++)
  Map[i][i]=0;
 for (int i=1;i<=P;i++)
 {
  int a,b;
  scanf("%d %d\n",&a,&b);
  Map[a][b]=-D;
 }
 for (int i=1;i<=F;i++)
 {
  int a,b,c;
  scanf("%d %d %d\n",&a,&b,&c);
  if(Map[a][b]==MAXN)
  {
   Map[a][b]=c-D;
  }
 }
 return;
}
QUEUE Q;
void SPFA()
{
 for (int i=1;i<=C;i++)
  dist[i]=MAXN,flag[i]=false,times[i]=0;
 dist[S]=-D;
 Q.push(S);
 int x;
 while(Q.size())
 {
  x=Q.front();
  Q.pop();
  flag[x]=false;
  for(int i=1;i<=C;i++)
  {
   int tmp=dist[x]+Map[x][i];
   if(tmpC)
     {
      printf("-1\n");
      return;
     }
    }
   }
  }
 }
 int Max=MAXN;
 for (int i=1;i<=C;i++)
  if(Max>dist[i])
   Max=dist[i];
 printf("%d\n",-Max);
 return;
}
int main()
{
 freopen("jobhunt.in","r",stdin);
 freopen("jobhunt.out","w",stdout);
 init();
 SPFA();
 return 0;
}

2011年12月17日 星期六

[二分查找]NOIP2011提高組:聰明的質檢員 qc 解題報告

【問題描述】
試題檢視:GoogleDocs
小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1 到 n 逐一編號,每個礦石都有自己的重量 wi 以及價值 vi。檢驗礦產的流程是:
1、給定 m個區間[Li,Ri];
2、選出一個參數 W;
3、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值 Yi : 




若這批礦產的檢驗結果與所給標準值 S 相差太多,就需要再去檢驗另一批礦產。小 T 不想費時間去檢驗另一批礦產,所以他想通過調整參數 W 的值,讓檢驗結果儘可能的靠近標準值 S,即使得 S-Y的絕對值最小。請你幫忙求出這個最小值。
【輸入】
輸入文件 qc.in。
第一行包含三個整數n,m,S,分別表示礦石的個數、區間的個數和標準值。
接下來的n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。
接下來的m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。注意:不同區間可能重合或相互重疊。
【輸出】
輸出文件名爲qc.out。
輸出只有一行,包含一個整數,表示所求的最小值。
【輸入輸出樣例】
qc.in
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
qc.out
10
【輸入輸出樣例說明】
當W 選4 的時候,三個區間上檢驗值分別爲20、5、0,這批礦產的檢驗結果爲25,此時與標準值S 相差最小爲10。
【數據範圍】
對於10%的數據,有1≤n,m≤10;
對於30%的數據,有1≤n,m≤500;
對於50%的數據,有1≤n,m≤5,000;
對於70%的數據,有1≤n,m≤10,000;
對於100%的數據,有1≤n,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1≤Li≤Ri≤n。

 【分析】
本題的正解是二分W,從0~MaxW+1二分。計算Y時要先預處理,把所有大於W的礦石記錄下來,然後再計算每個區間的值。

二分結束後,找出Min{Get(Right),Get(Left)}輸出即可。

【我的代碼】
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int N,M;
long long S;
const int MAXN=200001;
int W[MAXN];
int V[MAXN];
int L[MAXN];
int Sum[MAXN];
int R[MAXN];
long long TV[MAXN];
int TN[MAXN];
  
void init()
{
    Sum[0]=0;
    cin>>N>>M>>S;
    for (int i=1;i<=N;i++)
    {
        cin>>W[i]>>V[i];
        Sum[i-1]=W[i];
    }
    for (int i=1;i<=M;i++)
        cin>>L[i]>>R[i];
    sort(Sum,Sum+N);
    Sum[N]=Sum[N-1]+1;
}

long long Get(int w)
{
    long long res=0;
    TV[0]=0;
    TN[0]=0;
    for (int i=1;i<=N;i++)
    {
        if(W[i]>=w)
        {
            TV[i]=TV[i-1]+V[i];
            TN[i]=TN[i-1]+1;
            continue;
        }
        else
        {
            TV[i]=TV[i-1];
            TN[i]=TN[i-1];
        }
    }
  
    for (int i=1;i<=M;i++)
    {
        int tl=L[i];
        int tr=R[i];
        long long Temp=(TN[tr]-TN[tl-1])*(TV[tr]-TV[tl-1]);
        res+=Temp;
    }
  
    return res;
}

long long Abs(long long t)
{
    if(t>0)
        return t;
    else
        return -t;
}

void dichotomy()
{
    int Right;
    int Left;
    for (Left=0,Right=N;Left+1<Right;)
    {
        long long Temp=Get(Sum[(Right+Left)/2]);
        if(Temp>=S)
            Left=(Left+Right)/2;
        else
            Right=(Left+Right)/2;
    }
  
    long long TW1,TW2;
    TW1=Abs(Get(Sum[Right])-S);
    TW2=Abs(Get(Sum[Left])-S);
    if(TW2<TW1)
    {
        TW1=TW2;
    }
    cout<<TW1<<endl;
}  

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

2011年12月14日 星期三

動態規劃練習題:填加號 解題報告

【問題描述】
有一個由數字 1 , 2 , … , 9 組成的數字串(長度不超過 200 ),問如何將 M(M<=20) 個加號 (“+”) 插入到這個數字串中,使所形成的算術表達式的值最小。請編一個程序解決這個問題。 注意: 加號不能加在數字串的最前面或最末尾,也不應有兩個或兩個以上的加號相鄰。 M 保證小於數字串的長度。 例如:數字串 79846 ,若需要加入兩個加號,則最佳方案為 79+8+46 ,算術表達式的值 133 。
【輸入格式】
數字串在輸入文件的第一行行首(數字串中間無空格且不折行),M的值在輸入文件的第二行行首。
【輸出格式】
輸出所求得的最小和的精確值。
【輸入輸出樣例】

輸入:
exam4.in
82363983742
3
輸出:
exam4.out
2170

【分析】
動態規劃,類似於『NOIP2000 乘積最大』。
狀態設定:
F[i,j] :前i個數添加j個乘號的最小和
Num[i,j]:原數中第i個數到第j個數組成的新數。
邊界狀態:F[i,0]=Num[1,i]
狀態轉移方程: F[i,j]=Min{F[k,j-1]+Num[k+1,i]}
目標狀態:F[N,K]
由於數據比較大,所以需要高精度計算。

【我的代碼】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int SIZE=201;
int K;
int N;
typedef unsigned short int usint;
struct hugeint
{
 usint len,num[SIZE];
};
hugeint Num[201][201];
hugeint F[201][21];
void print(hugeint a)
{
 int i;
 for (i=a.len;i>=1;i--)
  printf("%d",a.num[i]);
 printf("\n");
}
hugeint add(hugeint a,hugeint b)
{
 int i;
 hugeint ans;
 memset(ans.num,0,sizeof(ans.num));
 if (a.len>b.len)
  ans.len=a.len;
 else
  ans.len=b.len;
 for (i=1;i<=ans.len;i++)
 {
  ans.num[i]+=(a.num[i]+b.num[i]);
  ans.num[i+1]+=ans.num[i]/10;
  ans.num[i]%=10;
 }
 if (ans.num[ans.len+1]>0)
  ans.len++;
 return ans;
}
bool over(hugeint a,hugeint b)
{
 int i;
 if (a.len<b.len)
  return false;
 if (a.len>b.len)
  return true;
 for (i=a.len;i>=1;i--)
 {
  if (a.num[i]<b.num[i])
   return false;
  if (a.num[i]>b.num[i])
   return true;
 }
 return false;
}
void init()
{
 hugeint tmp;
 memset(tmp.num,0,sizeof(tmp.num));
 char str[201];
 scanf("%s\n%d\n",&str,&K);
 int len=strlen(str);
 tmp.len=len;
 N=len;
 for (int i=0;i<len;i++)
 {
  tmp.num[i+1]=str[i]-'0';
 }
 for (int i=1;i<=len;i++)
 {
  for (int j=i;j<=len;j++)
  {
   memset(Num[i][j].num,0,sizeof(Num[i][j].num));
   Num[i][j].len=j-i+1;
   int top=Num[i][j].len;
   for (int k=i;k<=j;k++)
   {
    Num[i][j].num[top]=tmp.num[k];
    top--;
   }
   /*
   printf("%d %d %d ",i,j,Num[i][j].len);
   print(Num[i][j]);
   */
  }
 }
}
void dynamic()
{
 for (int i=1;i<=N;i++)
  F[i][0]=Num[1][i];
 for (int j=1;j<=K;j++)
 {
  for (int i=1;i<=N;i++)
  {
   for (int k=1;k<=N;k++)
    F[i][j].num[k]=9;
   F[i][j].len=N;
   for (int k=1;k<=i-1;k++)
   {
    hugeint temp=add(F[k][j-1],Num[k+1][i]);
    if(over(F[i][j],temp) )
    {
     F[i][j]=temp;
    }
   }
  }
 }
 print(F[N][K]);
}
int main()
{
 freopen("exam4.in","r",stdin);
 freopen("exam4.out","w",stdout);
 init();
 dynamic();
 return 0;
}

2011年12月11日 星期日

[動態規劃]OI練習題:乘法問題 [chf] 解題報告


【問題描述】
設有一個長度為 N 的數字字元串,分成 K+1 個部分,使得 K+1 個部 分的乘積最大。 例如 N=6 ,且數字字元串為 ‘ 310143 ‘ , K=3. 此時可能有的情況有以 下各種:
3 * 1 * 0 * 143=0
3 * 1 * 01 * 43=129
3 * 1 * 014 * 3=126
3 * 10 * 1 * 43=1290
3 * 10 * 14 * 3=1260
3 * 101 * 4 * 3=3636
31 * 0 * 1 * 43=0
31 * 01 * 4 * 3=372
310 * 1 * 4 * 3=3720
問題:當 N ,數字串, K 給出之後,找出一種分法使其乘積最大。
【輸入格式】
輸入由兩行組成,第一行有兩個整數,n(1≤n≤30)、k(1≤n≤30);n表示數字串長度、k表示乘號

個數。第二行是數字串。
【輸出格式】
輸出為一個整數,為乘積最大值。
【輸入樣例】
輸入文件名:chf.in
9 4
321044105
輸出文件名:chf.out
5166000
【分析】
動態規劃,和『NOIP2001 乘積最大』一樣。
狀態設定:
F[i,j]:前i個數字添加j個乘號時的最大值。
Num[i,j]:第i的數到第j個的數組成的數,可以在DP前用一個O(N^2)的預處理求出。
狀態轉移方程:
F[i,j]=Max(F[k,j-1]*Num[k+1][i])  (1<=k<=i-1)
目標狀態:  F[N,K]
時間複雜度:O(2*N^2)

【我的代碼】


#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; typedef long long LL; LL N,K; LL num[100][100]; LL F[100][100];   LL Max(LL a,LL b) { if (a>=b) return a; return b; }   LL myatoi(char *str) { LL ret=0; LL sign=1; if(*str=='-') sign=-1; else ret=ret*10+(*str-'0'); str++;   while(*str!= '\0') { ret=ret*10+(*str-'0'); str++; } return sign*ret; }   void init() { cin>>N>>K; char Temp[100]; cin>>Temp; char tmp[100]; for (unsigned int i=1;i<=strlen(Temp);i++) { for (unsigned int j=i;j<=strlen(Temp);j++) { unsigned int ti=i-1; unsigned int tj=j-1; memset(tmp,'\0',sizeof(tmp)); for (unsigned int k=ti;k<=tj;k++) tmp[k-ti]=Temp[k]; num[i][j]=myatoi(tmp); } } }   void dp() { for (int i=1;i<=N;i++) F[i][0]=num[1][i]; for (int j=1;j<=K;j++) { for (int i=1;i<=N;i++) { F[i][j]=0; for (int k=1;k<=i-1;k++) F[i][j]=Max(F[i][j],F[k][j-1]*num[k+1][i]); } } cout<<F[N][K]<<endl; }   int main() { freopen("chf.in","r",stdin); freopen("chf.out","w",stdout); init(); dp(); return 0; }

2011年12月10日 星期六

USACO Dec2011 Bronze: Escaping the Farm 翻譯+題解

Problem 3: Escaping the Farm [Brian Dean and Kalki Seksaria, 2011]
【英文原題】
The cows have decided on a daring plan to escape from the clutches of
Farmer John. They have managed to procure a small inflatable raft, and
during the cover of night, a group of cows will board the raft and row
across the river bordering the farm. The plan seems perfect, until the
cows realize that their small inflatable raft may not be able to hold
much weight!
The N cows (1 group of cows is light enough to avoid sinking the raft, the cows add up
all of the weights in the group. Unfortunately, cows are notoriously bad at
arithmetic, and if the addition of the weights of the cows in a group
causes any carries to occur (using standard base 10 addition), then the
cows give up and conclude that group must weigh too much to use the raft.
Any group whose weights can be added without any carries is assumed to be
light enough to fit on the raft.
Please help the cows determine the size of the largest group that they
believe can fit on the raft (that is, the largest group whose weights can
be added together with no carries).
PROBLEM NAME: escape
INPUT FORMAT:
* Line 1: The number of cows, N (1
* Lines 2..N+1: Each line contains the weight of one cow, an integer
in the range 1…100,000,000.
SAMPLE INPUT (file escape.in):
5
522
6
84
7311
19
INPUT DETAILS:
There are 5 cows, with weights 522, 6, 84, 7311, and 19.
OUTPUT FORMAT:
* Line 1: The number of cows in the largest group whose weights can be
added together with no carries.
SAMPLE OUTPUT (file escape.out):
3
OUTPUT DETAILS:
The three weights 522, 6, and 7311, can be added together with no carries:
522
6
+ 7311
——
7839

【中文翻譯】
Problem 3:逃離農場
譯by Freddy
【題目描述】
奶牛們做了一個魯莽的計劃:那就是逃離農場主Farmer John。她們已經獲得了一個可充氣的小型木筏,計劃在某天夜晚中,一群奶牛通過使用木筏渡而過位於農場邊界的河流。這個計劃似乎很完美,直到奶牛們意識到她們的小木筏可能不能承受住她們的體重。

這N頭奶牛(1<=N<=20)的體重w_1…w_N。為了計算出一群奶牛的體重能否避免木筏沉沒的悲劇,一群奶牛把她們的體重加在一起。
不幸的是,奶牛們在算術方面臭名遠揚,並且一群奶牛內的各奶牛體重相加的過程中如果出現了進位(標準的10進制),那麼這群奶牛只好放棄因為她們知道她們的體重對於小木筏來說太重了。

所有 那些群內奶牛體重相加不出現進位的奶牛群都被認為可以乘坐那個木筏而不發生沉沒。

請幫奶牛們找出能乘坐木筏而不沉沒的奶牛群的最大奶牛數。(也就是說,找出最多的奶牛使她們的體重相加而不出現進位。)

程序名:escape
輸入格式:
▪第1行:奶牛的數量,N(1<=N<=20)
▪第2…N+1行:每行包含一頭奶牛的體重,一個整數(1…100,000,000)。
輸入樣例(file escape.in):
5
522
6
84
7311
19
輸入解釋:
有5只奶牛,她們的體重分別為522,6,84,7311和19。
輸出格式:
只有一行,表示一群使她們的體重相加而不出現進位的奶牛的最大奶牛數量。
輸出樣例(file escape.out):
3
輸出解釋:



這三個奶牛的體重分別為:522,6,7311,它們相加不會出現進位:
    522
       6
+7311
 ——–
  7839

【分析】
搜索題目,可以用DFS。遞歸每頭牛的兩種狀態:要或者不要,每次遞歸判斷一下是否符合要求(沒有進位),然後更新最優值即可。
時間複雜度:
DFS遞歸:O(2^N)
判斷:O(KN) 『K是讀入的數字的平均長度,根據題意,可取5~6』

【我的代碼】
/*
ID:yeefan
LANG:C++
PROB:escape
website:http://yeefan.tk/
*/
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
int N;
int W[21];
int A=0;
int Q[21];
int Best=0;
void init()
{
 scanf("%d\n",&N);
 for (int i=1;i<=N;i++)
  scanf("%d\n",&W[i]);
 return;
}
void check()
{
 int T[11];
 memset(T,0,sizeof(T));
 for(int i=1;i<=A;i++)
 {
  int K=1;
  int tmp=W[Q[i]];
  while(tmp!=0)
  {
   T[K]+=tmp%10;
   tmp/=10;
   K++;
  }
 }
 for(int i=1;i<=10;i++)
 {
  if(T[i]>=10)
   return;
 }
 if(A>Best)
  Best=A;
}
void dfs(int num)
{
 if(num==N+1)
 {
  check();
  return;
 }
 Q[++A]=num;
 dfs(num+1);
 A--;
 dfs(num+1);
}
int main()
{
 freopen("escape.in","r",stdin);
 freopen("escape.out","w",stdout);
 init();
 dfs(1);
 printf("%d\n",Best);
 //printf("%d\n",clock());
 return 0;
}

2011年12月8日 星期四

【精華轉載】因為我們是OIer!

我們是OIer,

所以我們
不用在跑道上揮汗如雨;
不用在球場上健步如飛;
更不用在沒事的時候,
經受非人的體能訓練……
但是,
我們卻要把頭腦
高速運轉,
還要接受一大堆
大學生也只是
“瞭解即可”的知識,
把一個個抽象的問題
轉化為一篇篇
優美的代碼,
才能在F9按下以後
獲得歡呼。
不要以為我們
機房裡沒有風吹,
沒有日曬,
就比勤勞的體育生們輕鬆,
只不過是大腦和四肢
的區別罷了。
可是,
OIer的寂寞和委屈又有誰能懂?
自習課鏖戰機房,
卻被認為而是逃課上網;
為榮耀耽誤考試去比賽,
卻被認為是逃避。
體育的同學們雖然辛苦,
但在揮汗如雨的背後,
有人在喝彩鼓掌;
在風吹日曬的同時,
有粉絲在仰慕。
而我們呢?
與UnAC較勁的時候,
只有那一遍遍的運行窗口,
知道我們的不屈;
刷題的漫漫長夜,
只有陪伴我們的筆記本電腦,
知道我們的不懈;
在自習課別人學習的時候,
只有板磚般的算法導論,
知道我們的進取;
在機房泡得搶不上飯的時候,
只有五毛一包的乾脆面,
知道我們的執著……
沒有人會理解,
OIer見面,
除了程序、算法之外
別無他言。
我們的世界裡,
從來不會有遊戲、歌星的出現。
這不是被家長逼迫的“小三門”,
是我們的興趣,
我們的愛好,
乃至我們的事業。
每一個OIer都幻想著
自己脖子上可以
掛上一塊沉甸甸的金牌,
而不是
萬惡的應試教育的枷鎖。
沒準哪個OIer,
就是下一個艾倫•圖靈,
挑戰頭腦的極限,
去做最不平凡的自己。。。。。。

【精華轉載】假如沒有OI

假如沒有OI,我會像其他大多數人一樣擡頭看天,低頭看書,安靜的過完整個高中
假如沒有OI,我會一個人,一邊寫著作業,一邊做著肥皂般繁華的夢
假如沒有OI,我會輕鬆地擠進學校前100名,然後衣食無憂地等著高考地到來
假如沒有OI,我可以只用一半自習課的時間寫完所有作業,而不是晚上熬到深夜
假如沒有OI,我從來都不會知道矩陣,行列式,容斥原理等原本不屬於我們這個年齡的東西
假如沒有OI,我會花錢買一本《人間詞話》,而不是板磚一樣的《C++ primer》
假如沒有OI,我的空間裡會有一篇篇自己寫的詩或散文,而不是一堆解題報告
假如沒有OI,我永遠也不會聽說OIER這個名詞,也永遠不會去RQNOJ,BYVoid等網站
假如沒有OI,我在學校最常去的地方是圖書館,而不是機房
假如沒有OI,我的電腦裡會塞滿遊戲,而不是裝著IDE,編譯器和一包包的測試資料
假如沒有OI,我每天都會有大把的時間可以去放肆地揮霍
假如沒有OI,我永遠也不知道”犇“的讀音
假如沒有OI,我可能不會知道夢的含義,還有什麼是拼搏
假如沒有OI,我可能會和同學們一樣,週末去網吧聯機dota
假如沒有OI,我以後可能永遠也接觸不到演算法和編程
假如沒有OI,我會像所有的普通人一樣,平平淡淡,終其一生

NOIP2011提高組 Day2 計算係數 factor 解題報告

題目查看、下載

【題目分析】
數論,C(k,m)*a^n*b^m即可。C(k,m)表示組合數,可以使用楊輝三角遞推求出。
考察知識點:
1) 二項式定理
2) 組合數遞推/楊輝三角
3) 同餘定理


【我的代碼】

#include <cstdio>
#include <iostream>
#include <cstdlib>
using namespace std;
int A,B,K,N,M;
const int MOD=10007;
int YH[1020][1020];
void yanghui()
{
 for (int i=0;i<=K+1;i++)
  for (int j=1;j<=K+2;j++)
   YH[i][j]=0;
 for (int i=0;i<=K+1;i++)
  YH[i][1]=1;
 for (int i=1;i<=K;i++)
 {
  for (int j=2;j<=i+1;j++)
  {
   YH[i][j]=(YH[i-1][j-1]+YH[i-1][j])%MOD;
  }
 }  
 
 /*
 for (int i=0;i<=K;i++)
 {
  for (int j=1;j<=i+1;j++)
   printf("%d ",YH[i][j]);
  printf("\n");
 }
 */
}
 
int Get(int num,int index)
{
 int res=1;
 for (int i=1;i<=index;i++)
  res=(res*num)%MOD;
 return res;
}
 
int main()
{
 freopen("factor.in","r",stdin);
 freopen("factor.out","w",stdout);
 scanf("%d %d %d %d %d\n",&A,&B,&K,&N,&M);
 yanghui();
 int NM=Get(A%MOD,N);
 int MM=Get(B%MOD,M);
 int res=(NM*MM)%MOD;
 res=(res*YH[K][M+1])%MOD;
 //printf("%d %d\n",NM,MM);
 printf("%d\n",res);
 return 0;
}

2011年12月6日 星期二

【BFS】GZOI2011廣州2011選拔賽:理財年代 money 解題報告

【問題描述】
最近通貨膨脹很厲害,CPI跑得比銀行利息要快,要抗通脹,又要避風險,其中一種很好的方式,就是購買銀行發行的理財產品。雖然理財產品的利息比銀行定期要高,而且沒有風險,但是,購買理財產品需要一定的資金門檻,而且還要保證吧錢存入一定時間不能取出來,因此也是有一定的限制的。
小郭很喜歡研究銀行的理財產品,她計劃在2011年拿10萬元進行理財產品的投資,為了簡單方便,她在2011年每次投資理財產品時,都是把這筆資金和之前購買理財產品產生的所有利息投入進去,希望在年底獲取最高的利潤。
【理財產品】
一個理財產品有如下要素:
資金門檻:至少要投入多少資金;
發行時間:該理財產品的購買時間;
投資天數:資金存放的天數,
年利息:該理財產品如果存放一年365天能獲取的利息。
由於郭小姐選擇的所有理財產品的門檻都是10萬以內,因此理財產品就剩下的3個要素。
例如,A1理財產品,發行時間是3月1日,投資天數為30天,年利息為 3.5%,那麼,如果10萬元購買該產品,那麼在30天后,也就是3月30日收市後,她可以獲得的資金為:
`100000*(1+0.035*30/365)=100287.67元 (四捨五入,保留2位小數)
然後,她就可以吧100287.67元這筆資金,購買3月31日或之後發行的任何理財產品。
郭小姐在這一年內不斷把本金和利息一起全額地購買理財產品,希望在2012年到來之前獲得最高的收益。如果購買的兩個理財產品之間有時間間隔,那麼這筆錢就不能產生利潤(銀行活期利息太低,利潤可以忽略)。請問她這年內,能通過購買理財產品,最多獲取多少錢呢?
【輸入格式】
第一行是整數N(1<=N<=15),代表理財產品的數目
下面N行為3個由空格隔開的字元串 A B C
A代表發行時間,格式為MMDD(兩位月兩位日),例如4月1日則為0401,10月2日則為1002
B (整數),代表投資天數,範圍是[10,300]
C (最多2位的小數),代表百分之幾的年利息,範圍是[3,30]
輸入資料保證 發行時間+投資天數不會超過2012年。
【輸出格式】
輸出只有一行,為年底最多可獲得的連本帶利的資金數目,保留2位小數
【輸入樣例】
3
0101 100 4.5
0201 30 5
0402 50 7.8
【例子分析】
例子中的3個理財產品,只能購買1號產品,或者連續購買2號、3號理財產品。
購買1號理財產品的收益為 100000*(1+0.045*100/365)=101232.88
購買2/3號理財產品的收益為:
購買2號產品後總資金: 100000*(1+0.05*30/365)=100410.96
再購買3號產品後總資金: 100410.96*(1+0.078*50/365)=101483.84
因此最高收益為 101483.84
【輸出樣例】
101483.84

【分析】
本題是廣州NOI選拔賽中比較簡單的一道,可以用BFS廣度優先搜索做。由於N很小、而且減枝十分有效,所以隊列會很小。
本題易錯地方有:
1) 日期的轉換和判斷
2) 利息的計算

【我的代碼】
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int N;
class Money
{
public:
    int Mon;
    int Days;
    int Day1;
    int Day2;
    int Time;
    double Rate;
}M[30];

int days(int b,int c) 

    int d=0; 
    if(b==1) d=c; 
    if(b==2) d=31+c; 
    if(b==3) d=60+c; 
    if(b==4) d=91+c; 
    if(b==5) d=121+c; 
    if(b==6) d=152+c; 
    if(b==7) d=182+c; 
    if(b==8) d=213+c; 
    if(b==9) d=244+c; 
    if(b==10) d=274+c; 
    if(b==11) d=305+c; 
    if(b==12) d=335+c; 
    return d; 


int cmp(const void *a,const void *b)
{
    class Money *c=(class Money *)a;
    class Money *d=(class Money *)b;
    if(c->Day1!=d->Day1)
        return c->Day1-d->Day1;
    return c->Day2-d->Day2;
}

void init()
{
    scanf("%d\n",&N);
    int tmp1,tmp2;
    char ch1,ch2;
    for (int i=1;i<=N;i++)
    {
        scanf("%c%c",&ch1,&ch2);
        tmp1=(ch1-'0')*10+ch2-'0';
        scanf("%c%c",&ch1,&ch2);
        tmp2=(ch1-'0')*10+ch2-'0';
        M[i].Mon=tmp1;
        M[i].Days=tmp2;
        M[i].Day1=days(tmp1,tmp2);
        scanf("%d %lf\n",&M[i].Time,&M[i].Rate);
        M[i].Day2=M[i].Day1+M[i].Time-1;
    }
    qsort(M+1,N,sizeof(M[0]),cmp);
}

class QUEUE
{
public:
    double money;
    int pos;
    int day;
}Q[100000];

double GetMoney(double ben,int num)
{
    double res=ben;
    double rate=double(1)+M[num].Rate*(double(M[num].Time)/double(365))/(double(100));
    res=res*rate;
    return res;
}

void bfs()
{
    double S=0;
    int head=N;
    int rear=0;
    for (int i=1;i<=N;i++)
    {
        Q[i-1].pos=i;
        Q[i-1].money=GetMoney(double(100000),i);
        Q[i-1].day=M[i].Day2+1;
    }
   
    double tm;
    int td;
    int tp;
    while(rear<head)
    {
        tm=Q[rear].money;
       
        if(tm>S)
            S=tm;
       
        td=Q[rear].day;
        tp=Q[rear].pos;
        for(int i=tp+1;i<=N;i++)
        {
            if(M[i].Day1>=td)
            {
                Q[head].money=GetMoney(tm,i);
                Q[head].pos=i;
                Q[head].day=M[i].Day2+1;
                head++;
            }
        }
        rear++;
    }
    printf("%.2lf\n",S);
}


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

2011年12月1日 星期四

[最短路徑]NOIP模擬題:燃燒木棍 fire 解題報告

【問題描述】
Tom 是調皮的孩子,特別喜歡玩火,現在他手上有若干根長度分別爲 1 和sqrt(2) 的木棍,還有一張不能燃燒的平板,他把平板劃分成長度爲 1 的單元格,並標上座標。然後按以下規則把平板上的木棍組成一個連通圖:木棍的兩端必須放在單元格的頂點上。即長度爲 1 的木棍放在單元格的某一邊上,長度爲sqrt(2) 的木棍放在單元格的對角綫上。

Tom 的點火規則是,只能從木棍的兩端點火,而不能從木棍的中間點火。 如圖 ,不能在 A 點點火,但在 C 點或 B 點點火都是充許的。點火後,火會沿着木棍向前燃燒(每根都有自己的燃燒速度),並能點燃與它相接的其它木棍。
任務: 寫一程序計算從哪裏開始點火,使得燃燒的總時間最短,輸出最短時間。
Input Data
輸入文件第一行爲一個正整數 N ,表示組成圖形的木棍數目,後面共有 N 行,每行 5 個數: X1 Y1 X2 Y2 T , 其中( X1 , Y1 ) 和( X2 , Y2 )分別表示木棍兩端的座標, T 表示木棍燃燒時間,是指從木棍的某一端點火燃燒到別一端,燃完所需的時間。
Output Data
輸出文件是一個保留 4 位小數的實數,表示所有木棍完全燃燒的最少時間。
約束
1 ≤n≤40
保證圖形是連通的,所有的木棍長度都是 1 或sqrt(2) ,沒有任何兩根木棍重合 .
-200≤ X1, Y1, X2, Y2 ≤200; 0≤ T ≤ 10^7 .
如果你的輸出結果與標準答案相差小於 0.001 ,則認爲你的結果正確。
樣例 1
fire.in
1
0 0 1 1 1
fire.out
1.0000
解釋:從任一端點火都行,燃燒時間都是 1
樣例 2
fire.in5
0 0 0 1 1
1 0 0 1 10
0 0 1 0 1
0 0 1 1 1
2 2 1 1 1
fire.out 
3.2500
解釋:在 (0,0) 位置點火
木棍 1, 3 和 4 將被點燃,燃燒 0.5 分鐘後,木棍 2 將被從中間點燃向兩端燃燒,再過 0.5 分鐘,木棍 1, 3, 4 將被完全燃燒,木棍 5 將被點燃並在 1 分鐘後燃燒完 ( 比木棍 2 早燃完 ) 。
木棍 2 從中間向兩端燃燒 0.5 分鐘以後,變成兩小段,每段的燃燒時間是 4.5 分鐘。但因爲此時兩小段木棍的另一端也同時被點燃,燃燒速度變成原來的兩倍,還需 2.25 分鐘的燃燒時間, 所以總時間: 1 + 2 . 25 = 3 . 25
樣例 3
fire.in3
1 1 1 2 10
1 2 2 2 10
1 1 2 2 50
fire.out  
35.0000
解釋:在 (1,2) 位置點火, 木棍 (1 1, 1 2) 和 (1 2, 2 2) 將燃燒 10 分鐘。 . 最後一根木棍在 10 分鐘後從兩端被點燃,燃燒時間爲 25 分鐘。
【分析】
Floyd算法求最短路,只是構圖很困難罷了。
具體算法的PPT課件下載:http://zhuyifan.net84.net/ppt/fire.ppt
【我的代碼】
/*
*Problem: 燃燒木棍   fire
*Author: Yee-fan Zhu

*Date: Dec.01.2011
*Website: www.zyfworks.tk
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int N;

double max(double x,double y)
{
    if(y>x)
        x=y;
    return x;
}


class Wood
{
public:
    double x1,x2;
    double y1,y2;
    double time;
}F[42];
   
class Point
{
public:
    double x,y;
}P[1000];

Wood W[1000];
int D=0;
   
int M=0;
double Mat[1002][1002];   
int Used[2000][2000];
const int add=500;

void init()
{
    std::ios::sync_with_stdio(false);
    scanf("%d\n",&N);
    for (int i=-450;i<=1050;i++)
        for (int j=-450;j<=1050;j++)
            Used[i+add][j+add]=-1;
    for (int i=1;i<=300;i++)
        for (int j=1;j<=300;j++)
            Mat[i][j]=10000000;
    for (int i=1;i<=300;i++)
        Mat[i][i]=0;
    for (int i=1;i<=N;i++)
    {
        scanf("%lf %lf %lf %lf %lf\n",&F[i].x1,&F[i].y1,&F[i].x2,&F[i].y2,&F[i].time);
    }
    double x1,x2,y1,y2,t;
    for (int i=1;i<=N;i++)
    {
        x1=F[i].x1,y1=F[i].y1;
        x2=F[i].x2,y2=F[i].y2;
        t=F[i].time;
       
        int p1,p2;
        p1=Used[int(x1*2)+add][int(y1*2)+add];
        p2=Used[int(x2*2)+add][int(y2*2)+add];
        if(p1==-1)
        {
            M++;
            Used[int(x1*2)+add][int(y1*2)+add]=M;
            P[M].x=x1,P[M].y=y1;
            p1=M;
        }
       
        if(p2==-1)
        {
            M++;
            Used[int(x2*2)+add][int(y2*2)+add]=M;
            P[M].x=x2,P[M].y=y2;
            p2=M;
        }
       
        Mat[p1][p2]=t;
        Mat[p2][p1]=t;
        bool flag=true;
        if(F[i].x1-F[i].x2==0 || F[i].y1-F[i].y2==0)
        {
            D++;
            W[D].x1=x1,W[D].y1=y1;
            W[D].x2=x2,W[D].y2=y2;
            W[D].time=t;
            continue;
        }
        double x3,y3;
        double x4,y4;
        if((x1>x2 && y1>y2 )||(x1<x2 && y1<y2 ))
        {
            if(x1>x2 && y1>y2 )
            {
                x3=x1-1;
                y3=y1;
                x4=x1;
                y4=y1-1;
            }
            if(x1<x2 && y1<y2)
            {
                x3=x1;
                y3=y1+1;
                x4=x1+1;
                y4=y1;
            }
        }
        else
        {
            if(x1<x2 && y1>y2)
            {
                x3=x1;
                y3=y1-1;
                x4=x1+1;
                y4=y1;
            }
            if(x1>x2 && y1<y2)
            {
                x3=x1-1;
                y3=y1;
                x4=x1;
                y4=y1+1;
            }
        }
        for (int j=1;j<=N;j++)
        {
            if((F[j].x1==x3 && F[j].y1==y3 && F[j].x2==x4 &&F[j].y2==y4) || (F[j].x1==x4 && F[j].y1==y4 && F[j].x2==x3 &&F[j].y2==y3) )
            {
                double x5,y5;
                x5=double((x1+x2)/double(2));
                y5=double((y1+y2)/double(2));
                int p3=Used[int(x5*2)+add][int(y5*2)+add];
                if(p3==-1)
                {
                    M++;
                    p3=M;
                    P[M].x=x5,P[M].y=y5;
                    Used[int(x5*2)+add][int(y5*2)+add]=M;
                }
                Mat[p3][p1]=t/(double(2)),Mat[p1][p3]=t/(double(2));
                Mat[p2][p3]=t/(double(2)),Mat[p2][p3]=t/(double(2));
                D++;
                W[D].x1=x3,W[D].y1=y3;
                W[D].x2=x5,W[D].y2=y5;
                W[D].time=t/(double(2));
               
                D++;
                W[D].x1=x4,W[D].y1=y4;
                W[D].x2=x5,W[D].y2=y5;
                W[D].time=t/(double(2));
                flag=false;
                break;
            }
        }
       
        if(flag)
        {
            D++;
            W[D].x1=x1,W[D].y1=y1;
            W[D].x2=x2,W[D].y2=y2;
            W[D].time=t;
        }
    }
}

void Floyd()
{
    double tmp;
    for (int k=1;k<=M;k++)
    {
        for (int i=1;i<=M;i++)
        {
            for (int j=1;j<=M;j++)
            {
                tmp=Mat[i][k]+Mat[k][j];
                if(tmp<Mat[i][j])
                    Mat[i][j]=tmp;
            }
        }
    }
}

void work()
{
    double tot=100000000;
    for (int i=1;i<=M;i++)
    {
        double x,y;
        x=P[i].x;
        y=P[i].y;
        int tmp;
        tmp=int(x*2);
        if(tmp%2!=0)
            continue;
       
        int p=Used[int(x*2)+add][int(y*2)+add];
        double s=0;
        for (int j=1;j<=M;j++)
            s=max(s,Mat[p][j]);
       
        double x1,x2;
        double y1,y2;
        int p1,p2;
        double t1,t2;
        double dif;
        double need;
        for (int j=1;j<=D;j++)
        {
            x1=W[j].x1,y1=W[j].y1;
            x2=W[j].x2,y2=W[j].y2;
            p1=Used[int(x1*2)+add][int(y1*2)+add];
            p2=Used[int(x2*2)+add][int(y2*2)+add];
            t1=Mat[p][p1];
            t2=Mat[p][p2];
           
            double mint=t2;
            if(t1<mint)
                mint=t1;
               
            dif=max(t1-t2,t2-t1);
            if(dif>=W[j].time)
                continue;
            need=(W[j].time-dif)/(double(2));
            need=need+mint+dif;
            if(need>s)
                s=need;
        }
        if(s<tot)
            tot=s;
    }
    printf("%.4lf",tot);
}

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