2019 ICPC 南京网络赛 D.Robots

2019 ICPC 南京网络赛 D.Robots


题目给出n个点和m条有向边,机器人从1走到n,询问消耗的能量的期望。

如果只是求1到n的期望天数,就是很简单的套路题了,设dp[i]表示从i走到n的期望天数,容易得到dp[i]=p*dp[i]+(1-p)*dp[j]+1,其中p表示原地不动的概率。

比赛的时候求出天数之后就不会做了,没有细想,其实再叠加一曾,再做一次就可以了,设能量消耗是cost[i],一样有一个方程cost[i]=p *cost[i]+(1-p)*cost[j]+dp[i];

同样的方法做两次,服了。

写的时候利用拓扑,每次处理出度为0的点,一直处理到点1.

下面是AC代码:

#include <bits/stdc++.h>
using namespace std;
vector<int> a[100005];
vector<int> b[100005];
int vis1[100005];
int vis2[100005];
double dp[100005];
double cost[100005];
int main()
{

    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int j=1;while(j<=n)
        {
            a[j].clear();
            b[j].clear();
            vis1[j]=0;
            vis2[j]=0;
            dp[j]=0;
            cost[j]=0;
            j++;
        }
        int i=1;while(i<=m)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            a[y].push_back(x);
            b[x].push_back(y);
            vis1[x]++;
            vis2[x]++;
            i++;
        }
        vector<int>tt;
        tt.push_back(n);
        while(tt.size())
        {
            int temp=tt[0];
            tt.erase(tt.begin());
            int t1=b[temp].size();
            if(t1==0) dp[temp]=0;
            else
            {
                double sum=0;
                int k=0;while(k<t1)
                {
                    sum=sum+dp[b[temp][k]];
                    k++;
                }
                dp[temp]=1.0*(sum+t1+1)/t1;
            }

            int k=0;while(k<a[temp].size())
            {
                vis1[a[temp][k]]--;
                if(vis1[a[temp][k]]==0)
                {
                    tt.push_back(a[temp][k]);
                }
                k++;
            }
        }
        tt.push_back(n);
        while(tt.size())
        {
            int temp=tt[0];
            tt.erase(tt.begin());
            int t1=b[temp].size();
            if(t1==0) cost[temp]=0;
            else
            {
                double sum=0;
                int k=0;while(k<t1)
                {
                    sum=sum+cost[b[temp][k]];
                    k++;
                }
                cost[temp]=1.0*(sum+(t1+1)*dp[temp])/t1;
            }

            int k=0;while(k<a[temp].size())
            {
                vis2[a[temp][k]]--;
                if(vis2[a[temp][k]]==0)
                {
                    tt.push_back(a[temp][k]);
                }
                k++;
            }
        }

        printf("%.2lf\n",cost[1]);


    }
    return 0;
}

### 2019 ICPC亚洲银川区域B题相关信息与解法 根据提供的引用内容和问题,以下是关于2019ICPC亚洲银川区域B题的相关信息与解法。 #### 题目背景与目标 在2019ICPC亚洲银川区域中,B题被描述为涉及分段状态转移的动态规划问题。题目要求选手通过合理的状态设计和状态转移方程,计算出满足特定条件的最优解。具体来说,该问题的核心在于如何通过状态压缩和动态规划来优化时间复杂度[^3]。 #### 动态规划状态设计 为了求解此问题,可以定义一个三维动态规划数组 `dp[i][j][sta]`,其中: - `i` 表示当前处理到第 `i` 个元素。 - `j` 表示当前已经分成了 `j` 段。 - `sta` 表示当前最右端的状态,取值范围为 `{0, 1, 2}`,分别表示以下三种情况: - `sta == 0`:当前所有段都已经达到最优状态,即已经计算了最大值和最小值的贡献。 - `sta == 1`:当前段已经记录了最小值,但尚未找到最大值。 - `sta == 2`:当前段已经记录了最大值,但尚未找到最小值。 #### 状态转移方程 基于上述状态设计,状态转移方程可以分为以下几种情况: 1. 如果当前段的状态为 `sta == 0`,则可以选择开始一段新的区间或继续当前段: ```cpp dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j-1][0]); ``` 2. 如果当前段的状态为 `sta == 1`,则需要继续寻找最大值: ```cpp dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j][2] + (a[i] - min_val)); ``` 3. 如果当前段的状态为 `sta == 2`,则需要继续寻找最小值: ```cpp dp[i][j][2] = max(dp[i-1][j][2], dp[i-1][j][1] + (max_val - a[i])); ``` #### 边界条件与初始化 - 初始状态设置为 `dp[0][0][0] = 0`,其余状态均初始化为负无穷大。 - 最终答案可以通过遍历所有可能的段数 `j` 和状态 `sta` 得到: ```cpp int result = 0; for(int j = 1; j <= k; ++j){ result = max(result, dp[n][j][0]); } ``` #### 示例代码实现 以下是一个简单的代码框架,用于实现上述动态规划算法: ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 1e9; int dp[5010][510][3]; // i: 当前元素, j: 分段数, sta: 状态 int a[5010]; // 输入数组 int main(){ int n, k; cin >> n >> k; for(int i = 1; i <= n; ++i) cin >> a[i]; // 初始化 memset(dp, -0x3f, sizeof(dp)); dp[0][0][0] = 0; // 动态规划转移 for(int i = 1; i <= n; ++i){ for(int j = 0; j <= k; ++j){ for(int sta = 0; sta <= 2; ++sta){ if(dp[i-1][j][sta] == -INF) continue; if(sta == 0){ dp[i][j][0] = max(dp[i][j][0], dp[i-1][j][0]); if(j > 0) dp[i][j][1] = max(dp[i][j][1], dp[i-1][j-1][0] + a[i]); } else if(sta == 1){ dp[i][j][1] = max(dp[i][j][1], dp[i-1][j][1]); dp[i][j][2] = max(dp[i][j][2], dp[i-1][j][1] + a[i]); } else if(sta == 2){ dp[i][j][2] = max(dp[i][j][2], dp[i-1][j][2]); } } } } // 计算最终结果 int result = -INF; for(int j = 1; j <= k; ++j){ result = max(result, dp[n][j][0]); } cout << result << endl; return 0; } ``` #### 复杂度分析 - 时间复杂度:由于状态数量为 `O(n * k * 3)`,每次状态转移的时间复杂度为常数,因此总时间复杂度为 `O(n * k)`。 - 空间复杂度:使用了一个三维数组存储状态,空间复杂度为 `O(n * k * 3)`。 ---
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值