NOI 2004~2006

 

[NOI2005]聪聪与可可

参考:

https://wenku.baidu.com/view/90adb02acfc789eb172dc8a8.html

用n次spfa求出当聪聪在i位置,可可在j位置时,聪聪走的第一步。

同f[i][j]表示聪聪在i位置,可可在j位置时聪聪吃到可可的期望步数。

当聪聪在i位置,可可在j位置时,聪聪所走的路线是确定的(走距离可可最近的点(如果有多个走标号最小的))。

如果聪聪与可可在同一个位置,聪聪已经吃到可可了,期望为0.

那么就有:

1、f[i][j] =0 (i=j)

2、f[i][j] =1  (path[i][j]=j || path[path[i][j]][j]=j)

3、如果聪聪不能在一次选择中吃到可可,那么她吃到可可的期望就与可可所做的选择有关了

记忆化搜索实现。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=1010;
struct point
{
    int to;
    int nxt;
}edge[maxn*2];
int n,m,tot,S,T;
int head[maxn],p[maxn][maxn],dis[maxn],vis[maxn],du[maxn];
double f[maxn][maxn];

inline void add(int u,int v)
{
    tot++;
    edge[tot].to=v;
    edge[tot].nxt=head[u];
    head[u]=tot;
}

inline void spfa(int s)
{
    queue<int> q;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) dis[i]=1e9;
    dis[s]=0,vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int tt=q.front();
        q.pop();
        vis[tt]=0;
        for(int i=head[tt];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dis[v]>dis[tt]+1)
            {
                dis[v]=dis[tt]+1;
                p[v][s]=tt;
                if(!vis[v]) vis[v]=1,q.push(v);
                
            }
            else if(dis[v]==dis[tt]+1) p[v][s]=min(p[v][s],tt);
        }
    }
}

inline double dfs(int x,int y)
{
    if(x==y) return 0.0;
    if(p[x][y]==y || p[p[x][y]][y]==y) return 1.0;
    if(f[x][y]!=-1.0) return f[x][y];
    f[x][y]=0.0;
    for(int i=head[y];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        f[x][y]+=dfs(p[p[x][y]][y],v)/du[y];
    }
    f[x][y]+=dfs(p[p[x][y]][y],y)/du[y];
    f[x][y]+=1;
    return f[x][y];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) f[i][j]=-1.0;
    scanf("%d%d",&S,&T);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
        du[v]++,du[u]++;
    }
    for(int i=1;i<=n;i++) du[i]++;
    for(int i=1;i<=n;i++) spfa(i);
    printf("%.3lf\n",dfs(S,T));
    return 0;
}
View Code

 

 

[NOI2005]瑰丽华尔兹

f[i][j]表示从( i , j )开始的最长滑行距离,有f[i][j]=f[x][y]+dis( i , j , x , y );

因为不能拐弯,所以依照时间顺序,按照船的倾斜方向更新每一格的f值,取所有的f值的max

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=202;
char map[maxn][maxn];
pair<int,int> q[maxn],tmp;
int n,m,k,h,t,ans;
int dx[]={0,-1,1,0,0};
int dy[]={0,0,0,-1,1};
int f[maxn][maxn];

inline void solve(int xx,int yy,int dd,int T)
{
    int now=0;
    h=t=0;
    while(xx>=1 && xx<=n && yy>=1 && yy<=m)
    {
        if(map[xx][yy]=='x') h=t=0;
        else
        {
            tmp.first=f[xx][yy],tmp.second=now;
            while(h<t && q[t-1].first+now-q[t-1].second<=tmp.first) t--;
            q[t++]=tmp;
            while(h<t && (now-q[h].second)>T) h++;
            f[xx][yy]=q[h].first+(now-q[h].second);
            ans=max(ans,f[xx][yy]);
        }
        xx+=dx[dd],yy+=dy[dd];
        now++;
    }
}

int main()
{
    int x,y;
    scanf("%d%d%d%d%d",&n,&m,&x,&y,&k);
    for(int i=1;i<=n;i++)
        scanf("%s",map[i]+1);
    memset(f,0x80,sizeof(f)); f[x][y]=0;
    for(int i=1,s,e,d,len;i<=k;i++) 
    {
        scanf("%d%d%d",&s,&e,&d),len=e-s+1;
        if(d==1) for(int j=1;j<=m;j++) solve(n,j,1,len);
        if(d==2) for(int j=1;j<=m;j++) solve(1,j,2,len);
        if(d==3) for(int j=1;j<=n;j++) solve(j,m,3,len);
        if(d==4) for(int j=1;j<=n;j++) solve(j,1,4,len);
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

 

 

 

posted @ 2018-05-21 13:06  Captain_fcj  阅读(176)  评论(0编辑  收藏  举报