2017 Xian ACM Summer Training Warm-up Exercise 1

A题Chocolate

http://poj.org/problem?id=1322

题意:

c种巧克力,每种数量无穷大,取出n个放桌上,如果出现同种巧克力的就必须两个一起吃掉,即桌面上同种巧克力只能有0个或者1个。问取出n个后剩余m种巧克力的概率。

tip:

方程比较好想,就是dp[i][j]表示一共取了I次,桌子上还有j个的概率,那么
dp[i][j] = dp[i-1][j-1](拿到的是桌子上之前没有的颜色)(c-j+1)/c+dp[i-1][j+1](j+1)/c;
i,j范围都是1e5,那么数组都开不下,1)每个只与i-1项有关,所以可以滚动,且我们知道桌面上同种巧克力不可能出现多余1个,那么就是说j的范围就是100,但是外层循环i 还是会超时,考虑到每层递推都是一样的,可以用构造矩阵,矩阵快速幂即可。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int c,n,M;
const int maxn = 110;

struct Matrix{
    double m[maxn][maxn];
}a;
void print(Matrix a){
    for(int i = 0 ; i <= 5 ; i++){
        for(int j = 0 ; j <= 5 ; j++)
            printf("%.3f ",a.m[i][j]);
        printf("\n");
    }
    cout <<"\n";
}
void get_mar(){
    for(int i =  0 ; i <= c ; i++){
        for(int j = 0 ; j <= c; j++){
            if(i == 0 && j == 1)    a.m[i][j] = 1.0;
            else if(i == c && j == c-1) a.m[i][j] = 1.0;
            else if(j == i+1 )    a.m[i][j] = (double)(c-j+1)/c;
            else if(j == i-1)   a.m[i][j] =(double) (j+1)/c;
            else a.m[i][j] = 0.0;
        }
    }
    //print(a);
}

Matrix Mul_mar(Matrix a,Matrix b){
    Matrix p;
    for(int i = 0 ;i <= c ; i++)
        for(int j = 0 ; j <= c; j++)
            p.m[i][j] = 0.0;
    for(int i =  0 ;i <= c ;i++){
        for(int k = 0 ; k <= c ; k++)
            if(a.m[i][k])
                for(int j = 0 ; j <= c ; j++)
                    if(b.m[k][j])
                        p.m[i][j] = (p.m[i][j]+a.m[i][k]*b.m[k][j]);
    }
    return p;
}

Matrix quick_mar(int n){
    Matrix tmp;
    for(int i = 0 ;i <= c ; i++)
        for(int j = 0 ; j <= c; j++)
            tmp.m[i][j] = 0;
    for(int i = 0 ; i <= c ; i++)  tmp.m[i][i] = 1;
    while(n){
        if(n & 1)   tmp = Mul_mar(tmp,a);
        n >>= 1;
        a = Mul_mar(a,a);
    }
   // print(tmp);
    return tmp;
}

int main(){
    while(~scanf("%d",&c)&&c){
        scanf("%d%d",&n,&M);
        if(M > c){
            printf("0.000\n");
            continue;
        }
        get_mar();
        Matrix p = quick_mar(n);
        printf("%.3f\n",p.m[0][M]);
    }
}

B题Game Prediction :

http://poj.org/problem?id=1323

题意:

有M个人,一人N张牌,每轮牌面最大的人赢(牌面只可能是1~M*N中的一个数且不重复),给出一个人的牌,求其至少能够赢的局数。
##tip:
贪心,最开始觉得题意说的不明确,以为是博弈。。。后来并不知道别人怎么出牌,就只能从大到小,没有比自己大的一定赢了,有的话,手头最大的不一定赢,一直到没有才行

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
int m, n,ca;
const int maxn = 1100;
int cards[maxn];
void sov(){
    memset(cards, 0, sizeof(cards));
    for(int i = 0; i < n; i++){
        int t;
        scanf("%d", &t);
        cards[t] = 1;
    }
    int lar = 0, ans=0;
    for(int i = m*n; i > 0; i--){
        if(!cards[i]) lar++;
        else{
            if(lar == 0) ans++;
            else lar--;
        }
    }
    printf("Case %d: %d\n", ++ca,ans);
}
int main(){
    while(~scanf("%d%d",&m,&n) && m && n){
        sov();
    }
}

C题Holedox Moving :

http://poj.org/problem?id=1324

题意:

在n*m的地图上,给出长度为L的蛇身体各个节位置,以及有k个点是墙,问蛇从初始位置走到(1,1)点的最小步数。蛇不能撞墙,不能撞自己的身体。

tip:

看上去像是bfs,遇到问题:状态判重方法和已经每次图形中不可达点都在变。
神奇的hash,对整个图hash是肯定不行得了,考虑对身体hash,固定头的位置,已经身体上每个点对于上一块身体是在上下左右(2进制表达)哪个位置,2进制后,可以用位运算快速的知道移动后身体位置(我们知道后一块是前一快上次在的位置)。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
int n,m,l,ca;
struct node{
    int hx,hy;
    int body[8];
    int step;
};
int len;
queue<node> qq;
int dirx[]={0,0,1,-1};
int diry[]={1,-1,0,0};
int mp[22][22];
bool state[22][22][1<<14];
int trans(int x,int y){
    if (x == -1 && y == 0 )
        return 1;  //shang
    if (x == 1&&y == 0)
        return 2; //下
    if (x == 0 && y == -1)
        return 3 ;      //左
    if (x == 0 &&y == 1)
        return 4;   //右
}
node get_new(node t,int x,int y){
    node res;
    for (int i = len;i > 1;i--){
        res.body[i] = t.body[i-1];
    }
    int dir = trans(t.hx-x,t.hy-y);
    res.body[1] = dir;
    res.step = t.step + 1;
    res.hx = x;
    res.hy = y;
    return res;

}
void get_xy(int &tx,int &ty,int dir,int x,int y){
    if (dir == 1)//上
        tx = x-1,ty = y;
    if (dir == 2)//xia
        tx = x+1,ty = y;
    if (dir == 3)//zuo
        tx = x,ty = y-1;
    if (dir == 4)//you
        tx = x,ty = y+1;
}
void deal_map(node t){//body
    int lastx,lasty;
    for (int i = 1;i <= len;i++){
        int tx,ty;
        if (i == 1){
            get_xy(tx,ty,t.body[i],t.hx,t.hy);
        }
        else{
            get_xy(tx,ty,t.body[i],lastx,lasty);
        }
        lastx = tx;
        lasty = ty;
        if (mp[tx][ty] == 0)
            mp[tx][ty] = 2;
    }
}
void un_deal_map(node t){
    int lastx,lasty;
    for (int i = 1;i <= len;i++){
        int tx,ty;
        if (i == 1){
            get_xy(tx,ty,t.body[i],t.hx,t.hy);
        }
        else{
            get_xy(tx,ty,t.body[i],lastx,lasty);
        }
        lastx = tx;
        lasty = ty;
        if (mp[tx][ty] == 2)
            mp[tx][ty] = 0;
    }
}
int get_body_num(node y){
    int ret = 0;
    for (int i = 1;i <= len;i++){
        ret += (y.body[i]-1)* ( 1<<(2*(i-1)) );
    }
    return ret;
}
void bfs(){
    int ans = -1;
    while(!qq.empty()){
        node t=qq.front();
        qq.pop();
        if (t.hx == 1 && t.hy == 1 ){
            ans = t.step;
            break;
        }
        deal_map(t);        //设蛇身为障碍
        for (int i = 0;i < 4;i++){
            int x = t.hx+dirx[i];
            int y = t.hy+diry[i];
            if (mp[x][y]) continue;         //如果不可行
            node res = get_new(t,x,y);      //移动后得到的新状态res
            int ret=get_body_num(res);      //计算是否出现过当前状态
            if (state[res.hx][res.hy][ret] == true)
                continue;
            state[res.hx][res.hy][ret]=true;
            qq.push(res);
        }
        un_deal_map(t);     //恢复地图
    }

    printf("Case %d: %d\n",++ca,ans);
}
void init(){
    node tm;
    memset(mp,0,sizeof(mp));
    memset(state,0,sizeof(state));
    for (int i = 0;i <= m+1;i++)
        mp[0][i] = 1;
    for (int i = 0;i <= m+1;i++)
        mp[n+1][i] = 1;
    for (int i = 0 ;i <= n+1;i++)
        mp[i][0]=1;
    for (int i = 0;i <= n+1;i++)
        mp[i][m+1] = 1;
    int xx,yy;
    int lastx,lasty;
    int delx,dely;
    scanf("%d%d",&tm.hx,&tm.hy);
    for (int i = 1;i <= l-1;i++){
        scanf("%d%d",&xx,&yy);
        if (i == 1) {
            delx = xx - tm.hx;
            dely = yy - tm.hy;
        }
        else{
            delx = xx - lastx;
            dely = yy - lasty;
        }
        tm.body[i] = trans(delx,dely);
        lastx = xx;
        lasty = yy;
    }
    len = l-1;
    int num;

    scanf("%d",&num);
    for (int i = 1;i <= num;i++) {
        scanf("%d%d",&xx,&yy);
        mp[xx][yy] = 1;
    }
    tm.step = 0;
    while(!qq.empty())  qq.pop();
    int ret = get_body_num(tm);
    state[tm.hx][tm.hy][ret]=true;
    qq.push(tm);
}
int main(){
    while(~scanf("%d%d%d",&n,&m,&l)){
        if(n == 0 && m == 0 && l == 0)  break;
        init();
        bfs();
    }
}

D题Machine Schedule :

http://poj.org/problem?id=1325

题意:

有两个机器A和B,A机器有n个模式,B机器有m个模式,两个机器最初在0模式(wa点)
然后有k个作业,每个作业有三个参数i,a,b
i代表作业编号,a和b代表第i作业要么在A机器的a模式下完成【或者】在B机器的b模式下完成
问两个机器总共最少变换多少次可以完成所有作业

tip:

最小点覆盖,等于最大匹配,跑个匈牙利就好啦

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 10100;
int n,m,k,tot,head[maxn];
struct node{
    int to,next;
}edges[maxn];

void add(int u,int v){
    edges[tot].to = v;edges[tot].next = head[u];head[u] = tot++;
}
void init(){
    memset(head,-1,sizeof(head));
    tot = 0;
    for(int i = 1; i <= k ; i++){
        int l,r,jo;
        scanf("%d%d%d",&jo,&l,&r);
        if(l == 0 || r == 0)    continue;
        add(l,r+m);
    }
}
bool use[maxn];
int linker[maxn];

bool dfs(int u){
    int v;
    for(int k = head[u]; k != -1; k = edges[k].next){
        int to = edges[k].to;
        if(!use[to]){
            use[to] = true;
            if(linker[to] == -1 || dfs(linker[to])){
                linker[to] = u;
                return true;
            }
        }
    }
    return false;
}
void sov(){
    int res = 0;
    memset(linker,-1,sizeof(linker));
    for(int i = 1 ; i <= m ; i++){
        memset(use,0,sizeof(use));
        if(dfs(i)){
            res++;
        }
    }
    printf("%d\n",res);
}

int main(){
    while(~scanf("%d",&m)&&m){
        scanf("%d%d",&n,&k);
        init();
        sov();
    }

}

E - Mileage Bank

http://poj.org/problem?id=1326

题意:

给出航线 的长度,经济舱500公里以下算500公里,否则算actual mileage。商务舱算实际里程×1.5。头等舱实际里程×2。根据给出的航班,求出总的mileage bank中的值。

tip:

注意一下字符串结束的方式就好了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 11000;
char f[maxn],t[maxn],ty[2];
int dis,t2,t1,t3,flag;
void sov(){
    int ans = 0;
    while(1){
        scanf("%s",f);
        if(f[0] == '#'){
            flag = 1;
            return;
        }
        if(f[0] == '0') break;
        scanf("%s%d%s",t,&dis,ty);
        if(dis < 500){
            t1 = 500;
        }
        else   t1 = dis;
        t2 = dis+ (dis%2 == 0? dis/2:(dis+1)/2) ;
        t3 = dis+dis;
       // cout <<ty[0]<<"  t1 = "<<t1<<" "<<t2<<" "<<t3<<endl;
        if(ty[0] == 'Y') ans+=t1;
        else if(ty[0] == 'B')   ans+=t2;
        else ans+=t3;
       // cout <<"asn =  "<<ans<<endl;
    }
    printf("%d\n",ans);
    //if(flag == 1)   return;
}
int main(){
    while(1){
        sov();
        if(flag == 1)   break;
    }
}

G题G - Radar Installation:

http://poj.org/problem?id=1328

题意:

假设海岸线是一条无限延伸的直线。陆地在海岸线的一侧,而海洋在另一侧。每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。
题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。

tip:

每个点到能被x轴管辖的范围就是
t[i].first = x-sqrt(r*r-y*y);
t[i].second = x+sqrt(r*r-y*y);
就变成了最少选多少个点能使得所有区间内都有至少一个点,左端点排序,贪心

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n;
const int maxn = 1100;
typedef pair<double,double>pii;
pii t[maxn];
int ca = 0,flag;
double r,x,y;
void init(){
    flag = 0;
    for(int i = 0 ; i < n ; i++){
        scanf("%lf%lf",&x,&y);
        if(y > r)   flag = 1;
        t[i].first = x-sqrt(r*r-y*y);
        t[i].second = x+sqrt(r*r-y*y);
    }
    sort(t,t+n);

}
void sov(){
    int ans = 1;
    double r = t[0].second,l = t[0].first;
    for(int i = 1 ; i < n ; i++){
        if(t[i].second < r ){
            r = t[i].second;l= t[i].first;
        }
        else if(t[i].first <= r){
            l = t[i].first;
        }
        else{
            l = t[i].first;r = t[i].second;
            ans++;
        }
    }
    printf("Case %d: %d\n",++ca,ans);
}
int main(){
    while(~scanf("%d%lf",&n,&r)){
        if(n == 0 && r == 0)    break;
        if(n == 0){
            printf("Case %d: 0\n",++ca);
            continue;
        }
        init();
        if(flag == 1){
            printf("Case %d: -1\n",++ca);
            continue;
        }
        sov();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值