zoukankan      html  css  js  c++  java
  • 20171013校内训练

     

    我们先仔细阅读题目,发现你最多能解锁的房间和移动的次数是一样的。这样,每次我们就可以解锁你要走过的k个房间,然后往前走

    这样,我们会发现,从一个点开始,肯定是沿它往四个边界的距离的最小值(解锁完往前走就好了)走

    由于第一次你要先走,所以先处理出每个点到S的距离,对于所有距离<=k的点,我们找到它到边界的最小距离,计算出需要的步数(一次走k步),然后更新答案。

    记得最后答案要+1(因为你要先走一轮,然后才能解锁)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int step[1011][1011];
    int x[4]={0,-1,0,1};
    int y[4]={1,0,-1,0};
    int q[1001001][2];
    bool mp[1011][1011];
    char c[1010];
    int n,m,ans=999999999;
    void bfs(int xs,int ys)
    {
        memset(step,-1,sizeof(step));
        step[xs][ys]=0;
        int head=0,tail=0;q[tail][0]=xs;q[tail][1]=ys;tail++;
        while(head<tail)
        {
            int xi=q[head][0],yi=q[head][1];head++;
            for(int i=0;i<=3;i++)
            {
                int xx=xi+x[i],yy=yi+y[i];
                if(xx==0||yy==0||xx==n+1||yy==m+1)continue;
                if(!mp[xx][yy]&&step[xx][yy]==-1)
                {
                    step[xx][yy]=step[xi][yi]+1;
                    q[tail][0]=xx;q[tail][1]=yy;tail++;
                }
            }
        }
    }
    int main()
    {
    //    freopen("room.in","r",stdin);freopen("room.out","w",stdout);
        int sx,sy;
        int k;scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",c);
            for(int j=1;j<=m;j++)
            {
                if(c[j-1]=='#')mp[i][j]=1;
                if(c[j-1]=='S')mp[i][j]=0,sx=i,sy=j;
                if(c[j-1]=='.')mp[i][j]=0;
            }
        }
        bfs(sx,sy);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(step[i][j]<=k&&step[i][j]!=-1)
            {
                int Min=min(i-1,min(j-1,min(n-i,m-j)));
                if(Min%k==0)ans=min(ans,Min/k);
                else ans=min(ans,Min/k+1);
            }
        }
        printf("%d",ans+1);
        return 0;
    }
    View Code

    不要试图把它边权取反然后跑最小割(因为最小割是S-T割,S和T必须分在不同的连通块内,而本题不用)

    我们反过来思考,最后的图是连通的,这样,如果我们知道了最后的图,就能够算出边权和。

    因为边权和最大,所以最后的图必须是两颗树(如果不是树,那么肯定能在此图的基础上删去一些边后使得它是一棵树,这样删去的边权和更大),且最后剩的两棵树的边权和要尽量小。

    我们想到了什么?对,就是最小生成树。

    但是最小生成树是一棵树啊?我们在树中任意删去一条边,它不就变为两颗树了吗?

    为了使删去的边权和尽可能大,我们找到该最小生成树中边权最大的一条边,将其删去。

    这样,我们把总边权减去剩下的两棵树的边权即为答案

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    struct xxx{
        int u,v,cost;
    }g[101000];
    int n,m,Max=-1,res=0;
    int fa[101000];
    bool cmp(xxx a,xxx b){return a.cost<b.cost;}
    int Find(int x)
    {
        if(fa[x]==x)return x;
        return fa[x]=Find(fa[x]);
    }
    bool hb(int x,int y)
    {
        int xx=Find(x),yy=Find(y);
        if(xx==yy)return false;
        fa[xx]=yy;return true;
    }
    void kr()
    {
        sort(g+1,g+m+1,cmp);
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=m;i++)if(hb(g[i].u,g[i].v))res+=g[i].cost,Max=max(Max,g[i].cost);
    }
    int main()
    {
        freopen("cut.in","r",stdin);freopen("cut.out","w",stdout);
        int tot=0;scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&g[i].u,&g[i].v,&g[i].cost);
            tot+=g[i].cost;
        }
        kr();
        printf("%d",tot-(res-Max));
        return 0;
    }
    View Code

     期望DP。

    由于这是我第一次打期望DP,我写的详细一点。

    期望DP通常是逆推,即从结果推会初始。以此题为例。

    我们发现,这个期望刷墙次数和墙的顺序是没有关系的,即设一开始有一行两列,都没被粉刷,你刷第一列和第二列是一样的,都会有一列被粉刷且下一次刷没有被粉刷的那一列的概率相等

    这样,我们便可以用dp[i][j]表示有i行的墙上有格子被粉刷,有j列的墙上有格子被粉刷的期望粉刷次数。

    显然,dp[n][m]=0。设初始有x行的墙上有格子被粉刷,有y列的墙上有格子被粉刷,则我们要求的是dp[x][y]。

    我们思考如何转移。

    在dp[i][j]的基础上再粉刷一个格子(为了方便说明,我们把已刷的i行j列格子移至左上角)

    有(n-i)(m-j)/nm几率推出dp[i+1][j+1](即下一个刷的格子落在紫色区域)
    有(i*j)/nm几率不变(即下一个刷的格子落在绿色区域)
    有(n-i)j/nm几率推出dp[i+1][j](即下一个刷的格子落在蓝色区域)
    有i(m-j)/nm几率推出dp[i][j+1](即下一个刷的格子落在黄色区域)

    由于把dp[n][m]不刷某些格子后得到dp[x][y]的不刷的次数与把dp[x][y]刷某些格子后得到dp[n][m]的次数一致

    所以我们反过来思考,即把刚刚刷的格子删掉。

    若刚刚刷的格子落在紫色区域(概率为(n-i)(m-j)/nm),则把该格子不粉刷,i减小了1,j也减小了1,即dp[i][j]=dp[i+1][j+1]+1。

    若刚刚刷的格子落在绿色区域(概率为(i*j)/nm),则把该格子不粉刷,却没有减小i或j,即dp[i][j]=dp[i][j]+1。

    若刚刚刷的格子落在蓝色区域(概率为(n-i)j/nm),则把该格子不粉刷,i减小了1,j不变,即dp[i][j]=dp[i+1][j]+1。

    若刚刚刷的格子落在黄色区域(概率为i(m-j)/nm),则把该格子不粉刷,i不变,j减小了1,即dp[i][j]=dp[i][j+1]+1。

    综合起来看,dp[i][j]=dp[i+1][j]*(n-i)j/nm+dp[i][j+1]*i(m-j)/nm+dp[i][j]*(i*j)/nm+dp[i+1][j+1]*(n-i)(m-j)/nm+1

    去分母,nmdp[i][j]=dp[i+1][j](n-i)j+dp[i][j+1]*i(m-j)+dp[i][j]*(i*j)+dp[i+1][j+1]*(n-i)(m-j)+nm

    移项及合并同类项(把dp[i][j]移到左边去),dp[i][j]*(nm-ij)=dp[i+1][j](n-i)j+dp[i][j+1]*i(m-j)+dp[i+1][j+1]*(n-i)(m-j)+nm

    系数化为1,dp[i][j]=(dp[i+1][j](n-i)j+dp[i][j+1]*i(m-j)+dp[i+1][j+1]*(n-i)(m-j)+nm)/(nm-ij)

    这就是dp公式啦。

    总结一下:

    期望dp通常逆推,即从结果推向初始状态,也可以用记忆化搜索进行dp;

    E=Σp1*(E1+X1)+Σp2*(E+X2)

    其中E为当前状态的期望,E1为下一个状态的期望,p1和X1分别为将当前状态转移到下一个状态的概率和花费,p2和X2分别为保持当前状态的概率和花费。

    最后化简为E=(Σp1*(E1+X1)+Σp2*X2)/(1-Σp2)

    #include<cstdio>
    #include<iostream>
    using namespace std;
    double dp[1101][1101];
    int a[1101][1101];
    using namespace std;
    int main()
    {
    //    freopen("painter.in","r",stdin);freopen("painter.out","w",stdout);
        int ii=0,jj=0;
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(a[i][j]){ii++;break;}
        for(int j=1;j<=m;j++)
            for(int i=1;i<=n;i++)
                if(a[i][j]){jj++;break;}
        dp[n][m]=0.00;
        for(int i=n;i>=ii;i--)
        for(int j=m;j>=jj;j--)
        {
            if(i==n&&j==m)continue;
            dp[i][j]=((double)dp[i+1][j]*(n-i)*j+(double)dp[i][j+1]*i*(m-j)+(double)dp[i+1][j+1]*(n-i)*(m-j)+(double)n*m)/(double)(n*m-i*j);
        }
    //    for(int i=ii;i<=n;i++,cout<<endl)
    //    for(int j=jj;j<=m;j++)
    //    cout<<dp[i][j]<<" ";
    //    cout<<dp[ii][jj];
        printf("%.10lf",dp[ii][jj]);
        return 0;
    }
    View Code
  • 相关阅读:
    观察者模式学习--使用jdk的工具类简单实现
    观察者模式的初始学习--自己实现
    反射 reflect 初始学习
    eclipse java 空心J文件的回复
    linux 命令 more
    spring 3 的 @value的使用
    linux rm 命令
    linux log find 查询
    Resource is out of sync with the file system
    JavaScript(七)数组
  • 原文地址:https://www.cnblogs.com/lher/p/7662991.html
Copyright © 2011-2022 走看看