UVALive 3679 Pitcher Rotation(DP)

题目链接:点击打开链接

由于每个打手打完之后需要休息四天,从朴素的dp思路出发,我们只需要记录前四天选哪些打手就行了。

设dp[i][s1][s2][s3][s4]为从第1天到第i天,其中第i天选s4号打手,第i-1天选s3号打手,第i-2天选s2号打手,第i-3天选s1号打手的情况下的期望值。

状态转移方程为dp[i][s1][s2][s3][s4]=max(dp[i-1][s][s1][s2][s3]+p[i][s4]),其中s为枚举的第i-4天选的打手,在dp的时候需要保证s,s1,s2,s3,s4互不相同。

然而打手数最多会到100,如果这么设dp的话很显然时间和空间都会爆。

往贪心的方向想,假设打手不需要休息的话,那么每次都选胜率最高的打手出场就行了。

再假设打手需要休息一天,那么每次只需要从胜率最高的和胜率次高的两个选手中选一个就行了。

按照这个思想推下来,如果打手需要休息四天的话,那么每次比赛只需要从当前胜率最高的五个人中选一个就行了。

于是我们事先记录好当天每个人的胜率,然后降序排序,每次只需要从前五个中挑就行了。

于是s1,s2,s3,s4也可以改为表示选了某一天的第几高胜率的打手,状态转移也稍加修改就可以了。

对于不需要打比赛的时候,为了统一处理我们可以假设这个时候也需要打比赛。但是这些“比赛”又不能由题目给出的打手打。

对此,我们可以另外“创造”出五个打手,这些打手在所有比赛的胜率均为0(包括休息的时候),而我们假设题目给出的打手在不需要打比赛的时候的胜率为负无穷。

于是在dp过程中遇到不需要打比赛的时候这五个额外加进去的“打手”就会被用来填补空缺。

按照这样进行dp,最终取dp[g+10][s1][s2][s3][s4]中的最大值再除以100即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;

struct node
{
	int i,w;
	node(int ii=0,int ww=0):i(ii),w(ww){}
	bool operator <(const node &a)const
	{
		return a.w<w;
	}
};

int n,m,g;

vector<node> v[115];

int d[225];

int dp[225][7][7][7][7];

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&m,&g);
		g+=10;
		for(int i=0;i<=m;i++)
			v[i].clear();
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=n;j++)
			{
				int temp;
				scanf("%d",&temp);
				v[i].push_back(node(j,temp));
			}
			for(int j=1;j<=5;j++)
				v[i].push_back(node(n+j,0));
			sort(v[i].begin(),v[i].end());
		}
		for(int i=1;i<=5;i++)
			v[0].push_back(node(n+i,0));
		for(int j=1;j<=n;j++)
			v[0].push_back(node(j,-1000000));
		for(int i=1;i<=g;i++)
			scanf("%d",&d[i]);
		memset(dp,-1,sizeof(dp));
		for(int s1=0;s1<5;s1++)
			for(int s2=0;s2<5;s2++)
				for(int s3=0;s3<5;s3++)
					for(int s4=0;s4<5;s4++)
						dp[0][s1][s2][s3][s4]=0;
		for(int i=1;i<=g;i++)
			for(int s1=0;s1<5;s1++)
				for(int s2=0;s2<5;s2++)
				{
					if(i>3&&v[d[i-3]][s1].i==v[d[i-2]][s2].i)continue;
					for(int s3=0;s3<5;s3++)
					{
						if(i>3&&v[d[i-3]][s1].i==v[d[i-1]][s3].i)continue;
						if(i>2&&v[d[i-2]][s2].i==v[d[i-1]][s3].i)continue;
						for(int s4=0;s4<5;s4++)
						{
							if(i>3&&v[d[i-3]][s1].i==v[d[i]][s4].i)continue;
							if(i>2&&v[d[i-2]][s2].i==v[d[i]][s4].i)continue;
							if(i>1&&v[d[i-1]][s3].i==v[d[i]][s4].i)continue;
							for(int s=0;s<5;s++)
							{
								if(i>4&&v[d[i-3]][s1].i==v[d[i-4]][s].i)continue;
								if(i>4&&v[d[i-2]][s2].i==v[d[i-4]][s].i)continue;
								if(i>4&&v[d[i-1]][s3].i==v[d[i-4]][s].i)continue;
								if(i>4&&v[d[i]][s4].i==v[d[i-4]][s].i)continue;
								if(dp[i-1][s][s1][s2][s3]==-1)continue;
								dp[i][s1][s2][s3][s4]=max(dp[i][s1][s2][s3][s4],dp[i-1][s][s1][s2][s3]+v[d[i]][s4].w);
							}
//							printf("dp[%d][%d][%d][%d][%d]=%d\n",i,s1,s2,s3,s4,dp[i][s1][s2][s3][s4]);
						}
					}
				}
		int ans=-1;
		for(int s1=0;s1<5;s1++)
			for(int s2=0;s2<5;s2++)
				for(int s3=0;s3<5;s3++)
					for(int s4=0;s4<5;s4++)
						ans=max(ans,dp[g][s1][s2][s3][s4]);
		printf("%.2lf\n",(double)(ans)/100);
	}
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值