2013-5-7 训练赛总结

题目来源 The 13th Zhejiang University Programming Contest

链接: http://openoj.awaysoft.com:8080/judge/contest/view.action?cid=421#overview

 

A - Alien's Organ

题意: 事件出现平均概率为a, 问出现次数小于等于N的概率

解法:  泊松分布, P(X=k)=\frac{e^{-\lambda}\lambda^k}{k!} ,跟着公式算就好.

View Code
// p(k) = e^(-ave) * ave^k / k!
#include<cstdio>
#include<cmath>

int main(){
    double ave;
    int T, n;
    scanf("%d", &T);    
    while( T-- ){
        scanf("%d %lf", &n,&ave);    
        double p = 0, a = 1, b = 1;
        for(int i = 0; i <= n; i++){
            p += exp(-ave)*a/b;
            a *= ave;
            b *= (i+1);
        }
        printf("%.3lf\n", p );    
    }
    return 0;
}

 

B - Bad-written Number

题意:  LED灯表示一个数字,用了3*3行,   现在有N个数字, 前一个数字的第三列与后一个数字的第一列重叠,问可能出现合法的方案有多少.

解法:  状态压缩DP,  dp( i, j ), 表示 前i个数, 最后一个数的第三列状态j, 合法的方案数量. 转移方程为:  

  dp( i+1,  a[k][2] )  += dp( i, j ),  其中 a[k][ 0,1,2 ] ,表示数字 k,的第1,2,3列状态. 

View Code
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
typedef long long LL;

const int N = 20100;
const LL mod = (LL)1e9+7;

char str[3][N];

const char* tar[] = {
" _ | ||_|",
"     |  |",
" _  _||_ ",
" _  _| _|",
"   |_|  |",
" _ |_  _|",
" _ |_ |_|",
" _   |  |",
" _ |_||_|",
" _ |_| _|"
};
int a[10][3], b[N], n;
LL dp[N][4];

void pre(){
    memset( a, 0, sizeof(a) );    
    for(int i = 0; i <= 9; i++)    
        for(int j = 0; j < 3; j++)
            for(int k = 0; k < 3; k++)
                //a[i][j] |= (tar[i][j+3*k] != ' ') << k;
                a[i][j] = (a[i][j]<<1) | (tar[i][j+3*k] != ' ');
    //for(int i = 0; i < 10; i++)
    //    printf("%d: %d, %d, %d\n", i, a[i][0], a[i][1], a[i][2] );

}
void init(){
    memset( str, 0, sizeof(str));
    memset( b, 0 , sizeof(b));    
        
    for(int i = 0; i < 3; i++){
        gets( str[i] );    
        for(int j = strlen(str[i]); j < 2*n+1; j++)
            str[i][j] = ' ';
    }
    for(int j = 0; j < 3; j++)
        for(int i = 0; i < 2*n+1; i++)
            //b[i] |= (str[j][i] != ' ') << j;    
            b[i] = (b[i]<<1) | (str[j][i]!=' ');
}

void solve(){
    memset( dp, 0, sizeof(dp));
    dp[0][0] = 1;    
    for(int i = 0; i < n; i++){
        int idx = 2*i;    
        for(int j = 0; j < 4; j++){
            if( dp[i][j] == 0 ) continue;    
            for(int k = 0; k < 10; k++){
                if( (j | a[k][0]) != b[idx] ) continue;
                if( a[k][1] != b[idx+1] ) continue;
                if( (a[k][2] & b[idx+2]) != a[k][2] ) continue;
                dp[i+1][ a[k][2] ] += dp[i][j];
                dp[i+1][ a[k][2] ] %= mod;    
            }    
        }        
    }    
    printf("%lld\n", dp[n][ b[2*n] ]);
}
int main(){
    freopen("2.in","r",stdin);
    pre();
    int T;
    scanf("%d", &T);
    while( T-- ){
        scanf("%d", &n); getchar();
        init();
        solve();    
    }
    return 0;
}

 

C - Carrot Fantasy

题意: 很复杂- - ...和塔防一样.的模拟题.

解法: 按照时间模拟..不过好恶心.

 

D - Dakar Rally

题意: N段路, 每段路有个长度Li, 与单位耗油量ai,路段开始有个加油站,加油单价pi, 问按顺序走完所有路,最小花费.

解法: 贪心,  经过每一个加油站, 把油全部加满,  并利用单调队列保存, 这个油的数量与单价, 加油时,替换掉贵的, 使用时,从价格低的开始使用.

计算花费, 只算真正使用过的油.

View Code
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = (int)1e5+100;

int n; LL k;
struct TMP{
    LL a, b, c;
    void input(){
        scanf("%lld%lld%lld",&a,&b,&c);    
    }
}route[N];

struct node{
    LL gap, c;            
}Q[N<<1], pre;

bool init(){
    for(int i = 0; i < n; i++)
        route[i].input();
    for(int i = 0; i < n; i++)
        if( 1LL*route[i].a*route[i].b > k ) return false;
    return true;
}
void solve(){
    LL res = 0, s;
    int l = 0, r = 0; 
    for(int i = 0; i < n; i++){
        // clear and full the gap.
        while( (r>l) && (Q[r-1].c > route[i].c) ) r--;
        s = 0; 
        for(int j = l; j < r; j++) s += Q[j].gap;
        pre.c = route[i].c; pre.gap = k-s;    
        Q[r++] = pre;    
        // use the gap from the cheapest.
        s = 1LL*route[i].a*route[i].b;
        while( (l<r) && (Q[l].gap <= s) ){
            res += Q[l].gap*Q[l].c;
            s -= Q[l++].gap; 
        }
        if( s > 0 ){
            res += s*Q[l].c;    
            Q[l].gap -= s;    
        }    
    }
    printf("%lld\n", res);
}
int main(){
    int T;
    scanf("%d", &T);
    while( T-- ){
        scanf("%d%lld", &n,&k);
        if( init() ) solve();
        else printf("Impossible\n");
    }
    return 0;
}

 

E - Ever Dream

题意: 给N行字符, 然后统计单词出现频率. 输出频率大于1的, 最长单词,当有多个,则输出倒数第二个.

解法: 跟着模拟即可. 用STL来写.比较方便. 对于输入的处理, 将非英文字符全部换成空格,再利用strtok来分割.这样就蛮方便了.

View Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;

map<string,int> mp;


bool isletter( char ch ){
    if( ( (ch>='a')&&(ch<='z') ) || ( (ch>='A')&&(ch<='Z') ) )
        return true;
    return false;
}
bool Upletter( char ch ){
    if( (ch>='A') && (ch<='Z') )
        return true;
    return false;
}

struct node{
    string s;
    int len;
    bool operator < (const node &tmp) const{
        return (len>tmp.len)||((len==tmp.len)&&(s<tmp.s));    
    }
}nxt;
vector< node > Q[110];
void solve(){
    int n;
    char str[110];
    scanf("%d", &n); getchar();
    mp.clear();
    for(int i = 0; i < n; i++){
        memset(str, 0, sizeof(str));    
        gets( str );
        int L = strlen(str);
        for(int i = 0; str[i]; i++){
            if( isletter( str[i] ) ){
                if( Upletter( str[i] ) )
                    str[i] += 32;
            }
            else    str[i] = ' ';    
        }
        char *p = strtok( str, " " );
        while( p ){
            if( mp.count(p) == 0 ) mp[p] = 1;
            else    mp[p] += 1;
            p = strtok( NULL, " " );
        }    
    }
    for(int i = 0; i <= 100; i++)
        Q[i].clear();
    for( map<string,int>::iterator it = mp.begin(); it != mp.end(); it++ ){
        nxt.s = it->first;
        nxt.len = (it->first).size();
        Q[ it->second ].push_back( nxt );
    }
    for(int i = 0; i <= 100; i++)    
        sort( Q[i].begin(), Q[i].end() );
    bool flag = true;    
    for(int i = 100; i > 1; i-- ){
        if( (int)Q[i].size() == 0 ) continue;    
    /*    printf("len = %d, size = %d\n", i, Q[i].size() );    
        for(int j = 0; j < (int)Q[i].size(); j++ ){
            printf("%s ", Q[i][j].s.c_str() );    
        } puts("");    
    */    
        int cur = 1;    
        for(int j = 1; j < (int)Q[i].size(); j++ ){
            if( Q[i][j].len == Q[i][j-1].len ) cur++;
            else break;
        }    
        if( flag ) flag = false;
        else printf(" ");
        if( cur == 1 ) printf("%s", Q[i][0].s.c_str() );
        else printf("%s", Q[i][cur-2].s.c_str() );
    }
    puts("");
}
int main(){
    freopen("1.in","r",stdin);
    int T;
    scanf("%d", &T);
    while( T-- ){
        solve();    
    }
    return 0; 
}

 

F - Fawful's Revenge

据说此题很神....

 

G - Gibonacci number

题意: G(i) = G(i-1) + G(i-2), 现在给出 G(0) = 1,  G(i) , i, 让求 G(j), 当 G(1) > 0 , 否则输出 -1.

解法:  G(i) = F(i-1)*G(1) + F(i-2) , 带入计算即可. 注意判定是否合法.

View Code
#include<cstdio>

typedef long long LL;

LL f[25], G[25];

int main(){
    f[0] =  f[1] = 1;
    for(int i = 2; i <= 20; i++) f[i] = f[i-1]+f[i-2]; 
    int T, i, gi, j;
    scanf("%d", &T);
    while( T-- ){
        scanf("%d%d%d", &i,&gi,&j); // i, Gi, j
        
        if( i == 1 ){
            if( gi < 1 ){
                printf("-1\n"); continue;    
            }    
            if( j == 1 ) printf("%d\n", gi );
            else    printf("%lld\n", f[j-1]*gi+f[j-2] );
        }
        else{
            if( (gi-f[i-2])%f[i-1] != 0 ) printf("-1\n");
            else{
                LL g1 = (gi-f[i-2])/f[i-1];
                if( g1 < 1 ){
                    printf("-1\n"); continue;    
                }    
                if( j == 1 ) printf("%lld\n", g1);
                else printf("%lld\n", f[j-1]*g1+f[j-2] );    
            }
        }    
    }
    return 0;
}

 

H - Happy Programming Contest

题意:  N个题目,每个题目有个完成时间,有个价值,现在给定时间T,求最大完成数量,最小完成时间,最大价值.

解法: 01背包, 只是多了点条件

View Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1010;

int num[55][1010], dp[55][1100], time[55][1100], sum[55][1100];
int n, T;

struct node{
    int t, c;
    bool operator < (const node &tmp)const{
        return (t<tmp.t)||(t==tmp.t&&c>tmp.c);        
    }
}p[55];

int main(){
    int T, t;
    scanf("%d", &t);
    while( t-- ){
        scanf("%d%d", &T, &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", &p[i].t);
        for(int i = 1; i <= n; i++)
            scanf("%d", &p[i].c);
        sort( p+1, p+n+1 );
        memset( dp, 0, sizeof(dp));
        memset( num, 0, sizeof(num));
        memset( sum, 0, sizeof(sum));    
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= T; j++){
                // dp[i][j] = max( dp[i-1][j], dp[i-1][j-p[i].t]+p[i].c );
                dp[i][j] = dp[i-1][j]; num[i][j] = num[i-1][j];
                sum[i][j] = sum[i-1][j];    
                if( j >= p[i].t ){
                    if( dp[i][j] <= (dp[i-1][j-p[i].t]+p[i].c) ){
                        if( dp[i][j] < dp[i-1][j-p[i].t]+p[i].c ){
                            dp[i][j] = dp[i-1][j-p[i].t]+p[i].c;
                            num[i][j] = num[i-1][j-p[i].t]+1;
                            sum[i][j] = sum[i-1][j-p[i].t] + j;      
                        }    
                        else if( num[i][j] <= num[i-1][j-p[i].t]+1 ){
                             if( num[i][j] < num[i-1][j-p[i].t]+1 ){    
                                num[i][j] = num[i-1][j-p[i].t]+1;
                                sum[i][j] = sum[i-1][j-p[i].t] + j;    
                             }    
                             else if( sum[i][j] > sum[i-1][j-p[i].t] + j ){
                                 sum[i][j] = sum[i-1][j-p[i].t] + j;
                                }    
                        }    
                    }
                }    
            }
        }
        int pc = 0, pn = 0, pt = 0;
        for(int i = 0; i <= T; i++){
            if( pc < dp[n][i] ){
                pc = dp[n][i];pn = num[n][i];pt = sum[n][i];
            }    
            else if( pc == dp[n][i] ){
                if( pn < num[n][i] )
                    pn = num[n][i], pt = sum[n][i];    
                else if( pt > sum[n][i] )
                    pt = sum[n][i];
            }        
        }
        printf("%d %d %d\n", pc, pn, pt );    
    }
    return 0;
}

 

I - I am Nexus Master!

题意: 论坛有10个等级, 一个用户的等级评定,通过使用 注册时间,下载量,上传量, 上传/下载 比例来恒定.给你当前一个用户的信息,然后反馈其等级.

解法:  顺着模拟即可.  大致是 先判定是否直接降级到0, 否则再判定是否降级, 若没有 再判定是否升级. 

View Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const double esp = 1e-8;

const string tar[] = {
    "Peasant",         // 0
    "User",             // 1 
    "Power_User",     // 2
    "Elite_User",    // 3
    "Crazy_User",    // 4
    "Insane_User",   // 5
    "Veteran_User",     // 6
    "Extreme_User",  // 7
    "Ultimate_User", // 8
    "Nexus_Master"   // 9
};
double A[10]={
0,0,50,120,300,500,750,1024,1.5*1024,3*1024
};
double B[10]={
0,0,1.05,1.55,2.05,2.55,3.05,3.55,4.05,4.55    
};
int C[10]={
0,0,4,8,15,25,40,60,80,100
};
int sign(double x){
    return x<-esp?-1:(x>esp);
}
string s;
int week, cur;
double down, up;
void input(){
    char tmp[20];
    scanf("%s %d %lf %lf", tmp, &week, &down, &up );
    s = tmp;
    for(int i = 0; i < 10; i++) 
        if( s == tar[i] ){
            cur = i; break;    
        }
}
bool Clear(){
    if( (sign(down-50 ) >= 0) && (sign(up-0.4*down) < 0) ) return true;
    if( (sign(down-100) >= 0) && (sign(up-0.5*down) < 0) ) return true; 
    if( (sign(down-200) >= 0) && (sign(up-0.6*down) < 0) ) return true; 
    if( (sign(down-400) >= 0) && (sign(up-0.7*down) < 0) ) return true; 
    if( (sign(down-800) >= 0) && (sign(up-0.8*down) < 0) ) return true; 
    return false;    
}
bool Down(){ //可连续下降
    bool flag =  false;
    while( (cur>1) && (sign(up - down*(B[cur]-0.1)) < 0) )
        cur -= 1, flag = true;
    return flag;
}
void Up(){ //若降级过,则必定不可能升级
    int x = 9;    
    for(int x = 9; x > cur; x-- ){
        if( (x>cur) && (week>=C[x]) && (sign(down-A[x])>=0)
            && (sign(up-down*B[x])>=0) ){
            cur = x; break;
        }    
    }
}

int main(){
    int T;
    scanf("%d", &T);
    while( T-- ){
        input(); 
        if( Clear() ) printf("%s\n", tar[0].c_str() );
        else{
            if( Down() == false ) Up();
            printf("%s\n", tar[cur].c_str() );
        }
    }
    return 0;
}

 

 

posted @ 2013-05-09 13:40  yefeng1627  阅读(196)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor