POJ 刷题进程.1

POJ 刷题进程 .1-(已完结)

题外话:表示好久都没有更新过博客了,感觉前几个月学习压力有点大,现在放寒假了,就抽点时间发一下我们517竞赛教练给我的POJ刷题列表吧。

—————————————————手动分割———————————————————

POJ 1018 Communication System

题目传送门

题意:

简单来说,就是给你一个矩阵,总共有n*m个数据点,每个数据点有两个数据:带宽和费用,要求你从每一行都取出一个数据点,使得总共取出的n个数据点,最小的带宽长度除以费用之和最大,求这个最大值。

解题过程:

其实简单的分析一下题目就可以发现n,m的数据范围比较的小,于是我们可以简单的想到一种比较暴力的方法,就是枚举每一个数据点,以这个数据点的带宽作为最小值,然后暴力搜索这个数据点所在行之外的所有行,寻找每一行的带宽比选定的带宽大,且费用最小的数据点,得出答案之后再与最终答案ans取max就行了。

AC代码:

#include<cstdio>
#include<cmath>
#include<vector>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=105;
const double eps=1e-6;
typedef long long ll;

int T;
ll n,m[maxn],b[maxn][maxn],p[maxn][maxn];
double ans=0;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

inline void read(int &x) {
    x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    x*=f;
}

inline void read(ll &x) {
    x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=((x*10)+ch-'0'),ch=getchar();
    x*=f;
}


inline bool check(ll b,ll p)
{
    double xx=(double)b/(double)p;
    if(ans-xx>eps)return 1;
    return 0;
}

inline void solve2(ll l,ll minn,ll minp)
{ 
    ll res=minp;
    for(int i=1;i<=n;i++)
    {
        if(i==l)continue;
        ll minp=9e9;
        for(int j=1;j<=m[i];j++)
        {
            if(b[i][j]<minn)continue;
            minp=min(minp,p[i][j]);
        }
        res+=minp;
        // if(check((ll)minn,res))return ;
    }
    double x=(double)minn/(double)res;
    // printf("%lld %lld\n",minn,res );
    ans=max(ans,x);
}

inline void solve1()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m[i];j++)
        {
            solve2(i,b[i][j],p[i][j]);
        }
    }
} 


int main()
{
    // FO();
    read(T);
    while(T--)
    {
        ans=0;
        read(n);
        memset(b,0,sizeof b);
        memset(p,0,sizeof p);
        memset(m,0,sizeof m);
        for(int i=1;i<=n;i++)
        {
            read(m[i]);
            for(int j=1;j<=m[i];j++)
            {
                read(b[i][j]);
                read(p[i][j]);
            }
        }
        solve1();
        printf("%.3f\n",ans);

    }
    return 0;
}

POJ 1050 To the Max

题目传送门

题意:

求最大子矩阵的和,很基础的一道题。

解题过程:

可以定义一个前缀数组,pre[i,j]表示从[1,1]到[i,j]的矩形的数字之和,然后n的四次方枚举答案矩形的左上和右下两个点[x,y]和[j,k],用pre[x-1,y-1]+pre[j,k]-pre[x-1,k]-pre[j,y-1]就可以得到目标矩形的矩阵和(可以自己画图看一看)。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn=105;

int n;
int sum[maxn][maxn];
int pre[maxn][maxn];
int maxx=-1e9;

void init()
{
    memset(pre,0,sizeof pre);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            pre[i][j]=pre[i][j-1]+sum[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            pre[i][j]=pre[i-1][j]+pre[i][j];
        }
    }
}

inline void solve()
{
    for(int i=1;i<=n;i++) // x
    {
        for(int j=1;j<=n;j++)  // y
        {
            for(int k=i;k<=n;k++)  // z
            {
                for(int l=j;l<=n;l++)  // k
                {
                    int summ=0;
                    // printf("%d %d %d %d\n",pre[i-1][j-1],pre[k][l],pre[i-1][l],pre[k][j-1]);
                    summ+=pre[i-1][j-1]+pre[k][l];
                    summ=summ-pre[i-1][l]-pre[k][j-1];
                    maxx=max(maxx,summ);
                }
            }
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&sum[i][j]);
        }
    }
    init();
    solve();
    printf("%d\n",maxx);
}

POJ 1083 Moving Tables

题目传送门

题意:

总共有四百个房间,奇数在上方,偶数在下方,现在要在房间之间搬桌子,从i搬到j需要占用i到j这一个过道,同一时间只能有一张桌子同时占用一段过道(桌子在搬运的时候看作一直占用着这个过道),每搬一次桌子需要10分钟,问搬完所有桌子最少需要多少时间。

解题过程:

这道题看起来很像是一道贪心的题目,但实际上认真分析一下,就可以发现,对于每一个点i,占用这个点的次数是一定的,所以搬桌子的总次数一定是大于等于每个点被占用的次数的,所以在每一次搬桌子的过程中,把它所占用的区间的值都加1,最后统计出所有点的最大值,就是最少搬运次数。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=205;

int path[maxn];
int T;
int n;
int x,y;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int main()
{
    FO();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(path,0,sizeof path);
        for(int i=1;i<=n;i++)
        {
            int x,y;
            int st,en;
            scanf("%d%d",&x,&y);
            if(x&1)st=(x+1)/2;
            else st=x/2;
            if(y&1)en=(y+1)/2;
            else en=y/2;
            if(st>en)swap(st,en);
            for(int i=st;i<=en;i++)
            {
                path[i]++;
            }
        }
        int maxx=-1;
        for(int i=1;i<=200;i++)
        {
            maxx=max(maxx,path[i]);
        }
        printf("%d\n",maxx*10);
    }
    return 0;
}

POJ 1088 滑雪

题目传送门

题意:

简单来说,就是在一个矩阵中求最长下降子序列。

解题过程:

虽然说是求最长下降子序列,但实际上还是有点不同的。首先,题目没有给出我们滑雪开始的起点,所以需要我们去枚举起点,然后就会发现,在每次枚举计算的时候,实际上有很多计算是重复的,于是我们就可以比较自然的想到记忆化搜索。f[x][y]表示从[x,y]往后开始,能够得到的最长的下降子序列。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=105;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int f[maxn][maxn],mp[maxn][maxn];
int n,m;
int maxx=-1;

inline int dfs(int x,int y)
{
    if(~f[x][y])return f[x][y];
    if(mp[x][y]>mp[x+1][y])
    {
        if(dfs(x+1,y)+1>f[x][y])
        {
            f[x][y]=f[x+1][y]+1;
        }
    }
    if(mp[x][y]>mp[x][y+1])
    {
        if(dfs(x,y+1)+0>f[x][y])
        {
            f[x][y]=f[x][y+1]+1;
        }
    }
    if(mp[x][y]>mp[x-1][y])
    {
        if(dfs(x-1,y)+1>f[x][y])
        {
            f[x][y]=f[x-1][y]+1;
        }
    }
    if(mp[x][y]>mp[x][y-1])
    {
        if(dfs(x,y-1)+1>f[x][y])
        {
            f[x][y]=f[x][y-1]+1;
        }
    }
    return f[x][y];
}

int main()
{
    scanf("%d%d",&n,&m);
    memset(mp,0x3f,sizeof mp);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&mp[i][j]);
        }
    }
    memset(f,-1,sizeof f);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if ((mp[i-1][j]>=mp[i][j])&&(mp[i+1][j]>=mp[i][j])&&(mp[i][j-1]>=mp[i][j])&&(mp[i][j+1]>=mp[i][j]))
                f[i][j] = 1;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            maxx=max(dfs(i,j),maxx);
        }
    }
    printf("%d\n",maxx);
    return 0;
}

POJ 1125 Stockbroker Grapevine

题目传送门

题意:

一个人要传播一个消息,但消息只能在朋友之间传播,现在给你每个人的朋友编号以及相应的传播该消息所需要的时间,求把这条消息传播给所有人所需要的最短的时间。如果无法传播给所有人,就输出disjoint。

解题过程:

比较好发现这题实际上是个最短路,只需要根据所给的数据见图,然后用Floyd跑出任意两点之间的最短路,然后枚举每个人作为传播起点的情况,求出最短的传播时间就行了。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=130;
const int inf=0x3f3f3f3f;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int link[maxn][maxn];
int n,t;

inline void Floyd()
{  
    for(int k=1;k<=t;k++)  
    {  
        for(int i=1;i<=t;i++)
        {  
            for(int j=1;j<=t;j++)
            {  
               if(k==i||k==j||i==j)continue;  
               if(link[i][k]+link[k][j]<link[i][j])  
                link[i][j]=link[i][k]+link[k][j];  
            }  
        }  
    }  
}  

int minn=inf,maxx,people;

int main()
{
    while(scanf("%d",&t)&&t!=0)
    {
        memset(link,0x3f,sizeof link);
        for(int i=1;i<=t;i++)
        {
            scanf("%d",&n);
            link[i][i]=0;
            for(int j=1;j<=n;j++)
            {
                int to,cost;
                scanf("%d%d",&to,&cost);
                link[i][to]=cost;
            }
        }
        Floyd();
        minn=inf;
        for(int i=1;i<=t;i++)  
        {  
            maxx=0;  
            for(int j=1;j<=t;j++)
            {  
                if(link[i][j]>maxx)  
                    maxx=link[i][j];  
            }  
            if(maxx<minn)
            {
                minn=maxx;  
                people=i;  
            }  
        }  
        if (minn==inf)
            printf("disjoint\n");  
        else  
            printf("%d %d\n", people, minn); 
    }
    return 0;
}

POJ 1143 Number Game

题目传送门

题意:

你和你的朋友在玩一个游戏,刚开始你们有2~20这些数,每次你可以从这些数字中取出一个数,然后对于剩余的数,如果用已经被取出的数能够组成它们,那么这个数相应的也会被划掉。当一个人没有数字取的时候,另一个人就获胜了。现在给你一个残局,要求你输出能够获胜的所有方案。

解题过程:

比较容易的看出来这题的数只有2~20,所以可以想到用状态压缩,用每一位表示该位表示的数字是否已经被去用过。每一次取出数字之后,对于剩下的数字一一进行判断就行了。这题的状态数也比较少,所以很自然的就可以暴力的一一枚举出下一次对手会取出哪个数就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int N=20;

int f[2][1<<(N+1)];
int n,num[25];
int st;

int check(int state,int x)
{
    for (int i=0;i+x<=N;i++) 
    {
        if (state&(1<<i))
            state|=1<<(i+x);
    }
    return state;
}

inline int dfs(int state,int can)
{
    if(state==(1<<21)-1)
        return 1-can;
    int use=f[can][state];
    if (use)return use-1;
    for (int i=2;i<=N;i++) 
    {
        if (state&(1<<i))continue;
        use=dfs(check(state,i),1-can);
        if (can&&use)return 1;
        if (!can&&!use)return 0;
    }
    f[can][state]=2-can;
    return 1-can;
}

int main()
{
    int T=0;
    while(~scanf("%d",&n)&&n!=0)
    {
        T++;
        int x;
        st=((1<<(N+1))-1)&~2;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            st&=~(1<<x);
        }
        int tot=0;
        for(int i=2;i<=20;i++)
        {
            if(!(st&(1<<i)))
            {
                if(dfs(check(st,i),0))
                {
                    num[++tot]=i;
                }
            }
        }
        printf("Test Case #%d\n",T);
        if (tot) 
        {
            printf("The winning moves are:");
            for (int i=1;i<=tot;i++) 
            {
                printf(" %d", num[i]);
            }
            puts("");
        } 
        else 
        {
            printf("There's no winning move.\n");
        }
        puts("");
    }
    return 0;
}

POJ 1157 LITTLE SHOP OF FLOWERS

题目传送门

题意:

你有F束花和V个花瓶,每一种花插在每个花瓶中有着不同的美丽程度,要求你将这些花插在这些花瓶中,不改变原有的花的顺序,也不允许有两束花插在同一个花瓶中,求能够得到的最大的美丽程度。

解题过程:

可以想到这题是一个dp题,f[i,j]表示前i种花放在j个花瓶中所能得到的最大的美丽程度,于是可以想到转移方程为: f[i][j]=max(f[i][j],f[i-1][k]+b[i][j]) {k∈[1,j]}。这题还是比较基础的一道dp题。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=105;

int F,V;
int b[maxn][maxn];
int f[maxn][maxn];

int main()
{
    scanf("%d%d",&F,&V);
    for(int i=1;i<=F;i++)
    {
        for(int j=1;j<=V;j++)
        {
            scanf("%d",&b[i][j]);
        }
    }
    for(int i=1;i<=F;i++)
    {
        for(int j=1;j<=V;j++)
        {
            f[i][j]=-inf;
        }
    }
    for(int j=1;j<=V;++j)f[1][j]=b[1][j];  
    for(int i=2;i<=F;++i)
    {  
        for(int j=1;j<=V;++j)
        {  
            for(int k=1;k<j;++k)
            {  
                f[i][j]=max(f[i][j],f[i-1][k]+b[i][j]);  
            }  
        }  
    }  
    int ans=-inf;  
    for(int i=1;i<=V;++i){  
        ans=max(ans,f[F][i]);  
    }  
    printf("%d\n",ans); 
    return 0;
}

POJ 1163 The Triangle

题目传送门

题意:

你有一个数字三角形,每次往下一层时,只能走到和原位置相邻的两个位置,要求你找到一条路径,能够是的路径上的数字之和最大。

解题过程:

比较基础的dp,比较容易的就会想到转移方程:f[i][j]=max(f[i-1][j-1],f[i-1][j])+tri[i][j]。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=130;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int n;
int tri[maxn][maxn];
int f[maxn][maxn];

int main()
{
    scanf("%d",&n);
    memset(f,0,sizeof f);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            scanf("%d",&tri[i][j]);
        }
    }
    f[1][1]=tri[1][1];
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            f[i][j]=max(f[i-1][j-1],f[i-1][j])+tri[i][j];
            // printf("%d %d %d\n",i,j,f[i][j]);
        }
    }
    int maxx=-1e9;
    for(int i=1;i<=n;i++)
    {
        maxx=max(maxx,f[n][i]);
    }
    printf("%d\n",maxx );
    return 0;
}

POJ 1178 Camelot

题目传送门

题意:

给出一个国际象棋的棋盘,棋盘上有一个国王和若干个骑士,骑士走日字格,国王可以走九宫格之内的所有格,骑士遇到国王之后,可以带上国王,然后国王就会和骑士一起走日字格,且步数只算一步。要求国王和所有骑士走到同一个点的最少步数。

解题过程:

开始的时候没有什么思路,和班级里的dalao交流了一下之后,找到了方法,首先我们可以用Floyd或者dfs处理处骑士从每个点到达另外的点之间的最少步数。然后我们就可以开始暴力枚举两个点:国王与骑士相遇的点和终点。然后暴力枚举每个骑士走的路程,这样的复杂度是O(64^4)还算是比较优越的。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
using namespace std;
const int maxn=70;
const int inf=0x3f3f3f3f;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

char in[maxn];
int king_move[][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};  
int knight_move[][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};   
int knight[maxn][maxn],king[maxn][maxn];

int get_position(int x,int y)
{
    return x+y*8;
}

void get_xy(int i,int &x,int &y)
{
    x=i%8;
    y=i/8;
    return ;
}

bool check(int x,int y)
{
    if(x<0||x>=8||y<0||y>=8)return 0;
    return 1;
}

void init()
{
    // for(int i=0;i<8;i++)
    // {
    //     printf("%d %d\n",king_move[i][0],king_move[i][1] );
    // }
    for(int i=0;i<64;i++)
    {  
        for(int j=0;j<64;j++)  
        {
            king[i][j]=knight[i][j]=inf; 
        } 
        king[i][i]=knight[i][i]=0;  
    }  

    for(int i=0;i<64;i++)
    {  
        int x,y;
        int dx,dy;
        int nxt;
        get_xy(i,x,y); 
        for(int k=0;k<8;k++)
        {  
            dx=x+king_move[k][0];  
            dy=y+king_move[k][1];  
            if(check(dx,dy))
            {  
                nxt=get_position(dx, dy);  
                king[i][nxt]=1;  
            }  
            dx=x+knight_move[k][0];  
            dy=y+knight_move[k][1];  
            if(check(dx,dy))
            {  
                nxt=get_position(dx,dy);  
                knight[i][nxt]=1;  
            }  
        }     
    }  
}

void Floyd()
{
    for(int k=0;k<64;k++)
    {
        for(int i=0;i<64;i++)
        {
            for(int j=0;j<64;j++)
            {
                if(king[i][j]>king[i][k]+king[k][j])
                    king[i][j]=king[i][k]+king[k][j];
                if(knight[i][j]>knight[i][k]+knight[k][j])
                    knight[i][j]=knight[i][k]+knight[k][j];
            }
        }
    }
}

int point[maxn];

int main()
{
    scanf("%s",in);
    int n=strlen(in);
    int tot=0;
    memset(point,0,sizeof point);
    for(int i=0;i<n;i+=2)
    {
        point[tot++]=(in[i]-'A')+(in[i+1]-'1')*8;
    }
    init();
    Floyd();
    int ans=inf; 
    // for(int i=0;i<64;i++)
    // {
    //     for(int j=0;j<64;j++)
    //     {
    //         printf("%d %d %d %d\n",i,j,king[i][j],knight[i][j]);
    //         // system("pause");
    //     }
    // }
    int sum=0;
    for(int mid=0;mid<64;mid++)
    {  
        for(int m=0;m<64;m++)
        {  
            for(int k=1;k<tot;k++)
            {
                sum=0;  
                for(int i=1;i<tot;i++) 
                    sum+=knight[point[i]][mid];  
                sum+=king[point[0]][m];
                sum+=knight[point[k]][m]+knight[m][mid];  
                sum-=knight[point[k]][mid];  
                ans=min(ans,sum);  
            }  
        }  
    }  
    printf("%d\n",ans );
    return 0;
}

POJ 1179 Polygon

题目传送门

题意:

给你一个环,共n个点n条边,每个点有一个权值,每条边代表着一个加号或者乘号。刚开始你可以删去一条边,然后每次都要选择一条边,把这条边连着的两个点的权值按照该条边的运算符号计算结果,形成一个新的点的权值,要求最后剩下的一个点的权值的最大值,并且输出最大值刚开始删除边的方案。

解题过程:

还是一样,题目的数据比较小,所以刚开始删除的边可以通过枚举出来, 但之后的计算最大值就需要用到区间dp了,不过在区间dp的时候,要注意同时记录最大值和最小值,因为最大值可能是通过两个较小的负数相乘得到的。只要注意这一点,题目就应该不会太难了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;  
const int maxn = 122;  

char e[maxn][2];  
int val[maxn];  
int f1[maxn/2][maxn/2],f2[maxn/2][maxn/2];  
int res[maxn];  
int n;  
int maxans=-1e8;  

inline void solve(int k)
{
    for(int i=1;i<n;i++)
    {  
        for(int j=0;j+i<n;j++)
        {  
            int maxv=-1e8,minv=1e8;  
            for(int p=j;p<j+i;p++)
            {  
                if(e[p+k+1][0]=='t')
                {  
                    maxv=max(maxv,f1[j][p]+f1[p+1][j+i]);  
                    minv=min(minv,f2[j][p]+f2[p+1][j+i]);  
                }
                else
                {  
                    maxv=max(maxv,f1[j][p]*f1[p+1][j+i]);maxv=max(maxv,f1[j][p]*f2[p+1][j+i]);  
                    maxv=max(maxv,f2[j][p]*f1[p+1][j+i]);maxv=max(maxv,f2[j][p]*f2[p+1][j+i]);  
                    minv=min(minv,f1[j][p]*f1[p+1][j+i]);minv=min(minv,f1[j][p]*f2[p+1][j+i]);  
                    minv=min(minv,f2[j][p]*f1[p+1][j+i]);minv=min(minv,f2[j][p]*f2[p+1][j+i]);  
                }  
            }  
            f1[j][j+i]=maxv;  
            f2[j][j+i]=minv;  
        }  
    }  
    maxans=max(maxans,f1[0][n-1]);  
    res[k]=f1[0][n-1];
    return ;
}

int main()
{  

    scanf("%d",&n);  
    for(int i=0;i<n;i++)
    {  
        scanf("%s %d",e[i],&val[i]);  
        e[i+n][0]=e[i][0];  
        val[i+n]=val[i];  
    }  

    for(int k=0;k<n;k++)
    {  
        for(int i=0;i<n;i++)
        {  
            f1[i][i]=val[k+i];  
            f2[i][i]=val[k+i];   
        }  
        solve(k);
    }  
    printf("%d\n", maxans);  
    for(int i=0;i<n;i++)
    {  
        if(res[i]==maxans)
        {  
            printf("%d ",i+1);  
        }  
    }  
    puts("");
    return 0;  
}  

POJ 1189 钉子和小球

题目传送门

题意:

有一个三角形木板,上面有一些钉子,要求拆掉一些钉子之后,小球落到第m个篮子内的概率是多少。

解题过程:

乍一看有点像概率dp,实际上仔细想一想就可以发现,我们可以假设刚开始有2^n个小球从顶上放下,每次遇到钉子,等概率向左或向右,相当于一对小球分成两份,往不同方向走,于是就可以比较轻松的写出转移方程了,最后只需要统计一下落在第m个篮子内的小球数,除以小球总数就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=60;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int n,m;
ll f[maxn][maxn];
int mp[maxn][maxn];

ll gcd(ll x,ll y)
{
    if(x==0)return y;
    else return gcd(y%x,x);
}

int main()
{
    scanf("%d%d",&n,&m);
    memset(mp,0,sizeof mp);
    memset(f,0,sizeof f);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            char ch=getchar();
            while(ch!='*'&&ch!='.')ch=getchar();
            if(ch=='*')mp[i][j]=1;
            else mp[i][j]=0;
        }
    }
    f[1][1]=(1ll<<n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(f[i][j]>0)
            {
                if(mp[i][j]==1)
                {
                    f[i+1][j]+=f[i][j]/2;
                    f[i+1][j+1]+=f[i][j]/2;
                }
                else f[i+2][j+1]+=f[i][j];
            }
        }
    }
    // printf("%lld\n",f[n+1][m+1]);
    ll GCD=gcd(f[n+1][m+1],(1ll<<n));
    printf("%lld/%lld\n",f[n+1][m+1]/GCD,(1ll<<n)/GCD);
    return 0;
}

POJ 1208 The Blocks Problem

题目传送门

题意:

有一堆方块和四种操作,要求输出你在根据题目的操作之后,方块最终的放置情况。
操作:
①move a onto b:把a和b上方的方块都归原位之后,把a方块放在b方块上。
②move a over b:把a上方得方块都归原位之后,把a方块放在b方块顶部。
③pile a onto b:把b上方的方块都归原位之后,把a方块及a方块以上所有的方块一起放到b方块上(不改变顺序)。
④plie a over b:直接把a方块及a方块以上所有的方块一起放到b方块顶部(不改变顺序)。

解题过程:

题目比较简单,只需根据题意简单的模拟就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <vector>
using namespace std;
const int maxn=50;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int n;
vector<int>s[maxn];
char in1[maxn],in2[maxn];
int num1,num2;
int pos1,pos2;
int h1,h2;

void init()
{
    for(int i=1;i<=n;i++)
    {
        s[i].push_back(i);
    }
}

void find_block(int x,int y)
{

    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<(int)s[i].size();j++)
        {
            if(s[i][j]==x)
            {
                pos1=i;
                h1=j;
            }
            if(s[i][j]==y)
            {
                pos2=i;
                h2=j;
            }
        }
    }
    return ;
}

void block_clear(int h,int pos)
{
    for(int i=h+1;i<(int)s[pos].size();i++)
    {
        s[s[pos][i]].push_back(s[pos][i]);
    }
    while((int)s[pos].size()!=h+1)
    {
        s[pos].pop_back();
    }
    return ;
}

void get_command(int com,int x,int y)
{
    if(com==1)
    {
        block_clear(h1,pos1);
        block_clear(h2,pos2);
        s[pos1].pop_back();
        s[pos2].push_back(x);
    }
    if(com==2)
    {
        block_clear(h1,pos1);
        s[pos1].pop_back();
        s[pos2].push_back(x);
    }
    if(com==3)
    {
        block_clear(h2,pos2);
        for(int i=h1;i<(int)s[pos1].size();i++)
        {
            s[pos2].push_back(s[pos1][i]);
        }
        while((int)s[pos1].size()!=h1)
        {
            s[pos1].pop_back();
        }
    }
    if(com==4)
    {
        for(int i=h1;i<(int)s[pos1].size();i++)
        {
            s[pos2].push_back(s[pos1][i]);
        }
        while((int)s[pos1].size()!=h1)
        {
            s[pos1].pop_back();
        }
    }
    return ;
}

int main()
{
    // FO();
    scanf("%d",&n);
    init();
    while(scanf("%s",in1))
    {

        if(in1[0]=='q')break;
        if(in1[0]=='m')
        {
            scanf("%d",&num1);
            num1++;
            scanf("%s",in2);
            if(in2[1]=='n')
            {
                scanf("%d",&num2);
                num2++;
                find_block(num1,num2);
                if(pos1==pos2)continue;
                get_command(1,num1,num2);
            }
            if(in2[1]=='v')
            {
                scanf("%d",&num2);
                num2++;
                find_block(num1,num2);
                if(pos1==pos2)continue;
                get_command(2,num1,num2);
            }
        } 
        if(in1[0]=='p')
        {
            scanf("%d",&num1);
            num1++;
            scanf("%s",in2);
            if(in2[1]=='n')
            {
                scanf("%d",&num2);
                num2++;
                find_block(num1,num2);
                if(pos1==pos2)continue;
                get_command(3,num1,num2);
            }
            if(in2[1]=='v')
            {
                scanf("%d",&num2);
                num2++;
                find_block(num1,num2);
                if(pos1==pos2)continue;
                get_command(4,num1,num2);
            }
        }  
        // for(int i=1;i<=n;i++)
        // {
        //     printf("%d: ",i);
        //     for(int j=0;j<(int)s[i].size();j++)
        //     {
        //         printf("%d ",s[i][j]);
        //     }
        //     puts("");
        // }
        // system("pause");
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d: ",i-1);
        for(int j=0;j<(int)s[i].size();j++)
        {
            printf("%d ",s[i][j]-1);
        }
        puts("");
    }
    return 0;
}

POJ 1276 Cash Machine

题目传送门

题意:

你现在需要找钱,给出你需要找的钱数,然后给出你有的比值和纸币数量。问你能够找出的离所需要
找的钱数最近的钱数是多少。

解题过程:

比较容易看出来是个dp,dp[i]表示i价钱是否能够准确找回,然后n²dp就行了

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=100005;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

struct money
{
    int val;
    int cnt;
}m[20];

int n,tot;
bool f[maxn];
int maxval=0,c=0;

void DP()
{
    maxval=0;
    c=0;    
    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=maxval;j>=0;j--)
        {
            if(!f[j])continue;
            for(int k=1;k<=m[i].cnt;k++)
            {
                c=k*m[i].val+j;
                if(c>tot)continue;
                f[c]=1;
                if(c>maxval)
                {
                    maxval=c;
                }
            }
        }
    }
}

int main()
{
    // FO();
    while(~scanf("%d%d",&tot,&n))
    {
        memset(f,0,sizeof f);
        memset(m,0,sizeof m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d",&m[i].cnt,&m[i].val);
        }
        if(tot==0||n==0)
        {
            puts("0");
            continue;
        }
        DP();
        while(!f[tot])
        {
            tot--;
        }
        printf("%d\n",tot);
    }
    return 0;
}

POJ 1322 Chocolate

题目传送门

题意:

你有C种颜色的巧克力,现在你需要把巧克力取出来放在桌子上,但一旦桌子上出现了两种相同颜色的巧克力的时候,你就会把这两个巧克力都吃掉,问你在取出n个巧克力之后,桌子上剩余m个巧克力的概率是多少。

解题过程:

这道题目比较好,是一道需要懂点脑筋的概率dp题目。首先我么先判断一下概率为0的状态。当m>c或者m>n的时候,概率一定为0,这两种情况都是比较好想到的。还有一种是n+m为奇数的时候,概率也同样为0,为什么呢?我么可以分析一下,当拿出来一块巧克力时,如果它与已经取出的巧克力不同色,那么n和m都加一,如果是同色的,那么n加一,而m减一,所以无论如何,n+m的值一定都是偶数。
接下来讲一讲dp的思路,对于一个状态(i,j),表示取了i个巧克力出来,桌子上总共有j个巧克力的概率。而转移到这个状态共有两种方法:
1、对于状态(i-1,j+1),如果取出来的巧克力与桌子上的巧克力颜色相同,那么就可以转移到(i,j)状态,并且概率为( j+1)/c。
2、对于状态(i-1,j-1),如果取出来的巧克力与桌子上的巧克力颜色不相同,那么就可以转移到(i,j)状态,并且概率为( c-j-1)/c。
所以根据这个就可以比较简单的写出dp转移方程了,然而仅仅这样还是不够,题目中给出的n,m为100万,c为100,而我们的复杂度是O(cn)的,还有多组数据,所以铁定要TLE。这个时候我们就需要注意到题目中给出的答案的进度并不高,只有3位小数,所以我们就可以大胆的舍弃n大于1000的情况,对于1000以上的情况都只当做n=1000或1001的情况来考虑,因为当n大于1000的时候,贡献已经非常小了,所以可以忽略不计,这样的复杂度就可以稳稳的过了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef double db;

int c,n,m;
db f[2][105];

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int main()
{
    while(scanf("%d",&c)&&c!=0)
    {
        scanf("%d%d",&n,&m);
        memset(f,0,sizeof f);
        if(m>c||m>n||(m+n)&1)
        {
            puts("0.000");
            continue;
        }
        if(n>1000)n=1000+n%2;
        f[0][0]=(db)1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=i&&j<=c;j++)
            {
                f[i%2][j]=0;
                if((i+j)&1)continue;
                if(j>0)
                {
                    f[i%2][j]+=f[1-i%2][j-1]*(c-j+1)/c;
                }
                if(j+2<=i)
                {
                    f[i%2][j]+=f[1-i%2][j+1]*(j+1)/c;
                }
            }
        }
        printf("%.3f\n",f[n%2][m]);
    }
    return 0;
}

POJ 1414 Life Line

题目传送门

题意:

简单的说,就是在一个三角形中玩围棋游戏,在一个等腰三角形中,每一个节点都会与其他一些节点相连,刚开始所有的节点都是空的,每个人需要在棋盘上下棋,如果一个人的棋子没有气了,那么这些棋子就都会被吃掉(具体的规则和围棋很相似,但是不同的一点在于如果你自己的棋子在下下去的同时也没有气了,那么你自己的棋子也会损失掉)。每吃掉其他人的一颗棋子,你就会得一分,但每当你自己失去一颗棋子,就会扣一分。给你一个残局,问你能够得到最多的分数是多少。

解题过程:

这题还是一样,数据范围比较小,所以可以暴力模拟每一个下的位置,然后检查一下每一个点的棋子是否还有气,最后计算出得的分数就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=20;
typedef pair<int,int>P;
#define fi first
#define se second

void FO()
{
    // freopen("data.txt","r",stdin);
    // freopen("1.txt","w",stdout);
}

int n,num;
int mp[maxn][maxn];
int alive[maxn][maxn];
int dic[6][2]={{-1,-1},{-1,0},{0,-1},{0,1},{1,0},{1,1}};
vector<P>zero;
int ans=-1e6;

bool check(int x,int y)
{
    if(x<1||x>n||y<1||y>x)return 0;
    return 1;
}

inline void get_zero()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            int x=i,y=j;
            if(mp[x][y]==0)zero.push_back(P(x,y));
        }
    }
}

int vis[maxn][maxn];

inline void dfs(int numb,int x,int y)
{
    vis[x][y]=1;
    alive[x][y]=1;
    for(int k=0;k<6;k++)
    {
        int nx=x+dic[k][0];
        int ny=y+dic[k][1];
        if(!check(nx,ny))continue;
        if(vis[nx][ny])continue;
        if(mp[nx][ny]!=numb)continue;
        dfs(numb,nx,ny);
    }
    return ;
}

inline int get_died()
{
    memset(alive,0,sizeof alive);
    int goal=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            int x=i,y=j;
            int numb=mp[x][y];
            if(numb==0)
            {
                alive[x][y]=1;
                continue;
            }
            for(int k=0;k<6;k++)
            {
                int nx=x+dic[k][0];
                int ny=y+dic[k][1];
                if(!check(nx,ny))continue;
                if(!mp[nx][ny])
                {
                    alive[x][y]=1;
                    break;
                }
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            int x=i,y=j;
            if(!alive[x][y])continue;
            int numb=mp[x][y];
            memset(vis,0,sizeof vis);
            dfs(numb,x,y);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            if(alive[i][j])continue;
            // printf("i:%d j:%d %d\n",i,j,mp[i][j]);
            if(mp[i][j]!=num)goal++;
            else 
            {
                goal--;
                // puts("OPs");
            }
        }
    }
    return goal;
}


inline void solve()
{
    for(int i=0;i<(int)zero.size();i++)
    {
        P now=zero[i];
        mp[now.fi][now.se]=num;
        int goal=get_died();
        // puts("");
        // printf("%d %d\n",ans,goal );
        ans=max(ans,goal);
        mp[now.fi][now.se]=0;
    }
}

int main()
{
    // FO();
    while(scanf("%d%d",&n,&num)&&n!=0&&num!=0)
    {
        memset(mp,0,sizeof mp);
        memset(alive,0,sizeof alive);
        while(zero.size())zero.pop_back();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                scanf("%d",&mp[i][j]);
            }
        }
        get_zero();
        solve();
        printf("%d\n",ans );
        ans=-1e6;
    }
    return 0;
}

POJ 1456 Supermarket

题目传送门

题意:

给出你一个商店的n中商品的价值和下架时间,一种商品必须在下架时间之前销售才会获得其价值,并且销售一种商品需要一天的时间,问你如何安排销售顺序能够使获得的利润最多。

解题过程:

一个比较简单的贪心,只需要把每种商品的利润从小到大进行排序,然后按序进行安排就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=10005;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

struct product
{
    int val;
    int endtime;
}p[maxn];

int n,lasttime=-1;
int used[maxn];

bool cmp(product a,product b)
{
    if(a.val!=b.val)return a.val>b.val;
    else return a.endtime<b.endtime; 
}

int main()
{
//    FO();
    while(~scanf("%d",&n))
    {
        memset(used,0,sizeof used);
        memset(p,0,sizeof p);
        lasttime=-1;
        int totval=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i].val,&p[i].endtime);
            lasttime=max(lasttime,p[i].endtime);
        }
        sort(p+1,p+1+n,cmp);
        // for(int i=1;i<=n;i++)
        // {
        //     printf("%d %d\n",p[i].val,p[i].endtime);
        // }
        for(int i=1;i<=n;i++)
        {
            if(!used[p[i].endtime])
            {
                used[p[i].endtime]=1;
                totval+=p[i].val;
            }
            else 
            {
                for(int j=p[i].endtime-1;j>0;j--)
                {
                    if(!used[j])
                    {
                        used[j]=1;
                        totval+=p[i].val;
                        break;
                    }
                }
            }
        }
        printf("%d\n",totval);
    }
    return 0;
}

POJ 1458 Common Subsequence

题目传送门

题意:

给你两个字符串,问你这两个字符串的最长的公共子串有多长。

解题过程:

简单的求最长公共子序列的题,而且数据不大,可以n^2跑过。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=1050;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

char in1[maxn],in2[maxn];
int f[maxn][maxn];

int main()
{
    while(~scanf("%s",in1))
    {
        scanf("%s",in2);
        memset(f,0,sizeof f);
        int len1=strlen(in1);
        int len2=strlen(in2);
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                f[i][j]=max(f[i-1][j],f[i][j-1]);
                if(in1[i-1]==in2[j-1])
                    f[i][j]=max(f[i][j],f[i-1][j-1]+1);
                else f[i][j]=max(f[i][j],f[i-1][j-1]);
            }
        }
        printf("%d\n",f[len1][len2]);
    }
    return 0;
}

POJ 1609 Tiling Up Blocks

题目传送门

题意:

给出你一个序列,序列中每个点包含两个数,要求这两个序列同时的最长不下降子序列。

解题过程:

基本解题过程和LIS没有太大区别,只需要先对一个数组进行排序,然后对另一个数组进行nlongn的LIS就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=10050;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

struct numb
{
    int a,b;
}num[maxn];

int n;

bool cmp(numb x,numb y)
{
    if(x.a!=y.a)
    return x.a<y.a;
    else return x.b<y.b;
}

int stack[maxn];

int main()
{
    while(scanf("%d",&n))
    {
        if(n==0)
        {
            puts("*");
            return 0;
        }
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&num[i].a,&num[i].b);
        }
        sort(num,num+n,cmp);
        stack[0]=0;
        int top=0;
        for(int i=0;i<n;i++)  
        {  
            if(num[i].b>=stack[top])  
            stack[++top]=num[i].b;  
            else  
            {  
                int low=1,high=top;  
                int temp=num[i].b;  
                int mid;  
                while(low<=high)          
                {  
                    mid=(low+high)/2;  
                    if(stack[mid]<=temp)    
                    low=mid+1;  
                    else  
                    high=mid-1;  
                }  
                stack[low]=temp;  
            }  
        }  
        printf("%d\n",top);
    }
    return 0;
}

POJ 1644 To Bet or Not To Bet

题目传送门

题意:

给出一个由n个正方形组成的长为n的矩形,起点和终点分别在两边,每个正方形上都有一个数字,如果是负数就要向左移动相应的格子,是整数就要向右移动相应的格子,如果是0就不用动,是L则停止一回合。每次分别有0.5的概率向前走一格或两格,走到对应的格子之后要执行格子相应的指令,问你在T回合走到终点的概率是多少。

解题过程:

题目并不难,只需要根据题目的意思进行状态的转移就行了。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;
const int maxn=105;
const int eps=1e-6;

int T;
int n,m;
int stop[maxn];
int mp[maxn];
double f[maxn][maxn];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(stop,0,sizeof stop);
        memset(mp,0,sizeof mp);
        memset(f,0,sizeof f);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            char ch=getchar();
            while(ch!='L'&&ch!='0'&&ch!='-'&&ch!='+')ch=getchar();
            if(ch=='L')stop[i]=1;
            else if(ch=='0')mp[i]=0;
            else 
            {
                int x;
                scanf("%d",&x);
                if(ch=='-')x=-x;
                mp[i]=x;
            }
        }
        f[0][0]=(double)1;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<=n;j++)
            {
                if(f[i][j]>eps)
                {
                    if(stop[j+1])f[i+2][j+1]+=f[i][j]*0.5;
                    else f[i+1][j+1+mp[j+1]]+=f[i][j]*0.5;
                    if(stop[j+2])f[i+2][j+2]+=f[i][j]*0.5;
                    else f[i+1][j+2+mp[j+2]]+=f[i][j]*0.5;
                    // printf("%d %d %.4f\n",i,j,f[i][j] );
                }

            }
            f[i+1][n+1]+=f[i][n+1];
            f[i+1][n+2]+=f[i][n+2];

        }
        f[m][n+1]+=f[m][n+2];
        // printf("%.4f\n",f[m][n+1]-0.5);
        if(fabs(f[m][n+1]-0.5)<=eps)
        {
            puts("Push. 0.5000");
            continue;
        }
        if(f[m][n+1]-0.5>eps)printf("Bet for. %.4f\n",f[m][n+1]);
        else printf("Bet against. %.4f\n",f[m][n+1] );
    }
    return 0;
}

POJ 1664 放苹果

题目传送门

题意:

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。(中文题面,就直接拉过来了。。)

解题过程:

简单的递推。。

AC代码:

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cmath>  
#include <algorithm>  
#include <queue>
#include <set>
#include <vector>
using namespace std;

void FO()
{
    freopen("data.txt","r",stdin);
    freopen("1.txt","w",stdout);
}

int T;
int n,m;

inline int solve(int n,int m)
{
    if(n<m)return solve(n,n);
    if(n==1||m==1||m==0)return 1;
    return solve(n-m,m)+solve(n,m-1);
} 

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        int ans=solve(n,m);
        printf("%d\n",ans );
    }
    return 0;
}

第一套题就这么打完了,但是可怕的是我第二套题也做完了。。看来要花一天来整理博客了啊。。
Ended
2018年2月25日20:33:07

posted @ 2018-02-08 17:53  Apocrypha  阅读(186)  评论(0编辑  收藏  举报