ABC DAY two

题意分析:两个人分别等可能的前进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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值