题意分析:两个人分别等可能的前进1-p ,1-q,谁先到N谁获胜,求其中一位先走情况下获胜的概率
思路:概率dp dp i,j,0/1表示A,B分别位于i和j分的位置,0表示下一次是A的机会,1表示下一次是B的机会
为什么会用到三维呢:首先这道题目有两个对象,其次这两个对象有某种交替关系,所以要有一维表示次序
我们先考虑dp i,j,0的状态转化:A可以走1-p,并且走到其上的每一种可能性相同,可以反过来想,从那些前面的状态跳到当前状态,前面也是有p种跳跃的选择,然后最终跳到我们这个的概率就是其本身概率/p,所以dp i,j,0=1/pΣdp min(i+k,N),j,1
同理可以得到dp i,j,1=1/qΣdp i,min(j+k,N),0
解题:啊啊啊这道题目写出来+debug花了我快两个小时
1.对于这种题目,先假设最后AA获胜的概率为1,然后向前推,最后他走到A的概率,BB获胜概率为0,走到B的概率
2.所以初始定状态,就是先用循环把dp[n]p][1]/[0]=1;
3.后来对于每一个在后面的位置用循环进行状态转移
为什么会花这么久???
有个超级好用的求1/n的取模的费马小定理忘记啦
求1/p就是qmi(p,mod-2)
切记切记
还有就是你这个不能直接代替p本身,因为p还要用于循环,不然就会循环几十万次????
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int N=110;
const int mod= 998244353;
int dp[N][N][2];
int n,a,b,p,q;
int qmi(int a,int b)
{
int res=1;
while(b)
{
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void Add(int &a,int b)
{
(a+=b)%=mod;
}
signed main()
{
cin>>n>>a>>b>>p>>q;
int qmod=qmi(q,mod-2);
int pmod=qmi(p,mod-2);
//写法1
for(int i=0;i<n;i++)
{
for(int j=0;j<2;j++)
{
dp[n][i][j]=1;
dp[i][n][j]=0;
}
}
for(int i=n-1;i>=0;i--)
{
for(int j=n-1;j>=0;j--)
{
for(int k=1;k<=p;k++)
{
dp[i][j][0]=(dp[i][j][0]+dp[min(n,i+k)][j][1])%mod;
}
dp[i][j][0]=dp[i][j][0]*pmod;
dp[i][j][0]=dp[i][j][0]%mod;
for(int k=1;k<=q;k++)
{
dp[i][j][1]=(dp[i][j][1]+dp[i][min(n,j+k)][0])%mod;
}
dp[i][j][1]=(dp[i][j][1]*qmod)%mod;
}
}
//写法2
for(int i=n;i>=a;i--)
for(int j=n;j>=b;j--)
{
if(i==n)
{
dp[i][j][0]=dp[i][j][1]=1;
continue;
}
if(j==n)continue;
//2.1
for(int k=1;k<=p;k++)dp[i][j][0]=(dp[i][j][0]+dp[min(n,i+k)][j][1])%mod;
for(int k=1;k<=q;k++)dp[i][j][1]=(dp[i][j][1]+dp[i][min(n,j+k)][0])%mod;
//2.2
for(int k=1;k<=p;k++) Add(dp[i][j][0],dp[min(i+k,n)][j][1]);
for(int k=1;k<=q;k++) Add(dp[i][j][1],dp[i][min(j+k,n)][0]);
dp[i][j][0]=dp[i][j][0]*pmod%mod;
dp[i][j][1]=(dp[i][j][1]*qmod)%mod;
}
cout<<dp[a][b][0]<<endl;
}
题意:有一个非常非常大的稀疏矩阵,给出有数据的坐标和数据,求出来最大行列之和
分析:稀疏矩阵,行列之和--》行列标号与其sum相对应(为什么要对应呢?为了后面排序后的在查询)
求最大,不重不漏--》将行进行排序,用第一行与每列进行相加优先队列中的第一批,然后记录下其对应的sum-(r,c)值,并用它更新答案,然后行号加1,进入队列,最后就可以做到每一列和每一行不漏,同时那些值为0的也被跳过了
能找到就必须继续做:因为还要减去一个东西,并非真的就答案上优先
当无法在mp找到(r,c)那么就可以break,因为这是优先队列,后面的必然更加跟新不了,再次做最后一次行列相加
最多重复N次
代码:用map来映射行标号和行之和,列表号和列之和
用map<pair,int>来存坐标和值
用vector来存标号和其和(map-->pair)
为了排序之后还能找到原来的标号,在用原来的判断mp中有无
用array存和,行标,列标
优先队列来更新ans
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<unordered_map>
#include<array>
#define int long long
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define pi pair<int,int>
#define mp make_pair
using namespace std;
inline int read() {
char ch = getchar();
int x = 0, t = 1;
while ((ch < '0' || ch>'9') && ch != '-') { ch = getchar(); }
if (ch == '-') { t = -1, ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar(); }
return x * t;
}
int n;
map<int, int>R, C; map<pi, int>pc;
signed main() {
n = read();
rep(i, 1, n) {
int r = read(), c = read(), x = read();
R[r] += x, C[c] += x;//将第r行上增加x,并用map形成映射,第c列上增加x
pc[mp(r, c)] = x;//将坐标和对应的值形成映射
}
vector<pi>rr, cc;//用来存放{行标号,此行总和} {列标号,此列总和}
for (auto x : R) {
rr.push_back(x);//这个x竟然是pair,就是说map里面的关键字和键值自动形成pair
}
for (auto x : C) {
cc.push_back(x);
}
sort(rr.begin(), rr.end(), [](pi A, pi B) {return A.second > B.second; });//对于行进行排序,按照每行的总和从大到小
priority_queue<array<int, 3>>q;//优先队列,里面放的是一个三个值的array,分别是行+列总和,当前列
rep(i, 0, (int)cc.size() - 1) {
q.push({ cc[i].second + rr[0].second,i,0 });//优先队列,用最大的行上的总和加上各个列的总和,第i列,第0行
//放入所有的列+行最大数值,后面不断的将行替换
}
int ans = 0;
while (!q.empty()) {//优先队列按照第一个来排序
auto x = q.top();//当前最大的的,但是当减去当前坐标上的值之后就不一定了
q.pop();
if (pc.count(mp(rr[x[2]].first, cc[x[1]].first)))//x[2]:行号数(优先队列里面的) x[1]:列号数 rr[x[2]].first rr的first就是map的first,也就是行号数(转换成真实的,可以与pr相对应的)
// 同理,随后把这个行和列组合成pair,并且在有记录的pr里寻找
//如果有的话就可以更新,没有的话就不必要更新
{
ans = max(ans, cc[x[1]].second + rr[x[2]].second - pc[mp(rr[x[2]].first, cc[x[1]].first)]);
if (x[2] + 1 <= (int)rr.size() - 1) { q.push({ cc[x[1]].second + rr[x[2] + 1].second,x[1],x[2] + 1 }); }//更新下一个行号
}
else {
ans = max(ans, cc[x[1]].second + rr[x[2]].second);
break;
}
}
cout << ans << endl;
return 0;
}