codeforces 24D Broken Robot & XOR和路径 (高斯消元与后效性dp)

后效性dp入门~~

我发现我越来与热衷于写博客

codeforces 24D Broken Robot

个人认为这道题适合 去除后效性 入门,思维难度不大,适合复习高斯消元

题面:

你作为礼物收到一个非常聪明的机器人走在矩形板上。不幸的是,你明白它已经破碎并且行为相当奇怪(随机)。该板由N行和M列单元组成。机器人最初位于第i行和第j列的某个单元格中。然后在每一步,机器人都可以去另一个细胞。目的是走到最底层(N.排。机器人可以停留在当前单元格中,向左移动,向右移动或移动到当前单元格下方的单元格。如果机器人位于最左侧的列中,则它不能向左移动,如果它位于最右侧的列中,则它不能向右移动。在每一步中,所有可能的动作都是同样可能的。返回预期的步数以到达最下面一行。

分析:

先列方程式
设f[i][j]表示从(i,j)这个点走到第n行的期望步数
首先当i=n是f[n][j]=0
当j=1时机器人只能在原地或往下或往右
f [ i ] [ 1 ] = 1 / 3 ∗ ( f [ i ] [ 2 ] + f [ i ] [ 1 ] + f [ i + 1 ] [ 1 ] ) + 1 ( i &lt; n ) f[i][1]=1/3*(f[i][2]+f[i][1]+f[i+1][1])+1 (i&lt;n) f[i][1]=1/3(f[i][2]+f[i][1]+f[i+1][1])+1(i<n)
当j=m是机器人只能在原地或往下或往左
f [ i ] [ m ] = 1 / 3 ∗ ( f [ i ] [ m ] + f [ i ] [ m − 1 ] + f [ i + 1 ] [ m ] ) + 1 ( i &lt; n ) f[i][m]=1/3*(f[i][m]+f[i][m-1]+f[i+1][m])+1 (i&lt;n) f[i][m]=1/3(f[i][m]+f[i][m1]+f[i+1][m])+1(i<n)
否则四种情况
f [ i ] [ j ] = 1 / 4 ∗ ( f [ i ] [ j ] + f [ i ] [ j − 1 ] + f [ i ] [ j + 1 ] + f [ i + 1 ] [ j ] ) + 1 ( i &lt; n , 1 &lt; j &lt; m ) f[i][j]=1/4*(f[i][j]+f[i][j-1]+f[i][j+1]+f[i+1][j])+1 (i&lt;n,1&lt;j&lt;m) f[i][j]=1/4(f[i][j]+f[i][j1]+f[i][j+1]+f[i+1][j])+1(i<n,1<j<m)
但是这样的话会有后效性,所以高斯消元
移项一下
例如
f [ i ] [ j ] = 1 / 4 ∗ ( f [ i ] [ j ] + f [ i ] [ j − 1 ] + f [ i ] [ j + 1 ] + f [ i + 1 ] [ j ] ) + 1 f[i][j]=1/4*(f[i][j]+f[i][j-1]+f[i][j+1]+f[i+1][j])+1 f[i][j]=1/4(f[i][j]+f[i][j1]+f[i][j+1]+f[i+1][j])+1
1 / 4 ∗ ( f [ i ] [ j − 1 ] + f [ i ] [ j + 1 ) − 3 / 4 ∗ f [ i ] [ j ] = − 1 / 4 ∗ f [ i + 1 ] [ 1 ] − 1 1/4*(f[i][j-1]+f[i][j+1)-3/4*f[i][j]=-1/4*f[i+1][1]-1 1/4(f[i][j1]+f[i][j+1)3/4f[i][j]=1/4f[i+1][1]1
可以发现f[i+1][1]是不受影响已知的,并且这就像一个方程
于是就可以将f[i][1…m]看成m个位置数,并且相应的有m个方程,系数和常数都可以计算
然后高斯消元求解
但是高斯消元是 o(m^3)
可以发现有m个未知数,但每个方程都只涉及2~3个未知数,类似于这样(1表示有系数,0表示没有)
1 1 0 0 0
1 1 1 0 0
0 1 1 1 0
0 0 0 1 1
然后就可以发现对角线上都有系数,那就不用去找了
消么可以先消成上三角矩阵,然后在消一遍(我努力想一遍消完,没做到)
注意m=1时要特判

代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,x,y;
double f[1010][1010],c[1010][1010],b[1010];
void build()
{
	for (int i=1;i<=m;i++)
	{
	  if (i==1) c[i][1]=-(2.0/3.0),c[i][2]=1.0/3.0;
	  else if (i==m) c[i][m-1]=1.0/3.0,c[i][m]=-(2.0/3.0);
	  else c[i][i-1]=1.0/4.0,c[i][i]=-(3.0/4.0),c[i][i+1]=1.0/4.0;
    }
}
int main()
{
	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&x,&y);
	if (m==1) {printf("%lf",(double)(n-x)*2);return 0;}
	for (int i=1;i<=m;i++)
	  f[n][i]=0;
	for (int i=n-1;i>=1;i--)
    {
    	build();
		for (int j=1;j<=m;j++)
		   if (j==1||j==m) b[j]=-f[i+1][j]/3-1;
		   else b[j]=-f[i+1][j]/4-1;
		for (int j=1;j<m;j++)
		{
	       	double k=c[j+1][j]/c[j][j];
	        c[j+1][j]=0;
	        c[j+1][j+1]-=k*c[j][j+1];
	        b[j+1]-=k*b[j];
	    }
	    for (int j=m;j>1;j--)
	    {
	    	double k=c[j-1][j]/c[j][j];
	    	c[j-1][j]=0;
	    	b[j-1]-=b[j]*k;
	    }
	    for (int j=1;j<=m;j++)
	      f[i][j]=b[j]/c[j][j];
	}
	printf("%lf",f[x][y]);
	return 0;
}

XOR和路径

这个好像思维难度也不大

题目

给定一个无向连通图,其节点编号为1到N,其边的权值为非负整数。

试求出一条从1号节点都N号节点的路径,使得该路径上经过的边的权值的XOR和最大。

该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算XOR和时也应被重复计算相应多的次数。

直接求解上述问题比较困难,于是你决定使用非完美算法。

具体来说,从1号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿着这条边走到下一个节点,重复这个过程直到走到N号节点为止,便得到一条从1号节点到N号节点的路径。

显然得到每条这样的路径的概率是不同的,并且每条这样的路径的XOR和也不一样。

现在请你求出该算法得到的路径的XOR和的期望值。

n<=100

分析

貌似不可以小数进行xor运算??

考虑吧xor和分解成二进制

最终得到的xor和路径不会超过2^30,所以分别考虑每一位,计算最后的到的xor和中这一位是1的概率,对答案的贡献就是概率*(1<<x) (期望公式??)

考虑对于第x位时的dp方程
f[i]表示从第i个点走到点n xor和第x位是1的概率,那么第x为是0的概率就是1-f[i]
d[i]表示点i的度,点j为与i相连的点,w(i,j)表示点i到点j的边权二进制下第x位的值
f [ i ] = 1 / d [ i ] ∗ ( ∑ w ( i , j ) = 1 ( 1 − f [ j ] ) + ∑ w ( i , j ) = 0 f [ j ] ) f[i]=1/d[i]*(\sum_{w(i,j)=1}(1-f[j])+\sum_{w(i,j)=0}f[j]) f[i]=1/d[i](w(i,j)=1(1f[j])+w(i,j)=0f[j])
于是得到方程
d [ i ] ∗ f [ i ] − ∑ w ( i , j ) = 0 f [ j ] + ∑ w ( i , j ) = 1 f [ j ] = ∑ w ( i , j ) = 1 1 d[i]*f[i]-\sum_{w(i,j)=0}f[j]+\sum_{w(i,j)=1}f[j]=\sum_{w(i,j)=1}1 d[i]f[i]w(i,j)=0f[j]+w(i,j)=1f[j]=w(i,j)=11
方程右边是常数,对于每个点都列一个方程
然后就高斯消元
本题数据n<=100,直接上就好了
需要注意的是
f[n]=0,不能再从其它点转移
所以n的方程就是1*f[n]=0

自环只要连一条边,度数值算1(据说自环都是这样的,但我不知道。。。)

#include <bits/stdc++.h>
using namespace std;
int t,Link[110],n,m,d[110];
double c[110][110],b[110],ans;
struct dsa
{
    int nex,v,w;
}e[20010];
void insert(int xx,int yy,int zz)
{
    e[++t].nex=Link[xx];
    e[t].v=yy;
    e[t].w=zz;
    Link[xx]=t;
}
void build(int x)
{
    memset(c,0,sizeof(c));
    for (int i=1;i<n;i++)
    {
        int s=0;
        c[i][i]=(double)d[i];
        for (int j=Link[i];j;j=e[j].nex)
          if ((e[j].w>>x)&1) c[i][e[j].v]++,s++;
            else c[i][e[j].v]--;
        b[i]=s;
    }
    c[n][n]=1;b[n]=0;
}
double gause()
{
    for (int i=1;i<=n;i++)
    {
        for (int j=i;j<=n;j++)
          if (fabs(c[j][i])>1e-8)
          {
            for (int k=1;k<=n;k++) swap(c[i][k],c[j][k]);
            swap(b[i],b[j]);
            break;
          }
        if (fabs(c[i][i])<1e-8) continue;
        for (int j=1;j<=n;j++)
        {
            if (i==j) continue;
            double h=c[j][i]/c[i][i];
            for (int k=i;k<=n;k++)
              c[j][k]-=h*c[i][k];
            b[j]-=b[i]*h;
        }
    }
    return b[1]/c[1][1];
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int xx,yy,zz;
        scanf("%d%d%d",&xx,&yy,&zz);
        insert(xx,yy,zz),d[xx]++; 
        if (xx!=yy) insert(yy,xx,zz),d[yy]++;
    }
    for (int i=0;i<=30;i++)
    {
        build(i);
        double x=gause();
        ans+=x*(1<<i);
    }
    printf("%.3lf",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值