SRM605题解(T3除外)

T1:

题目大意:给定n个物品,每个物品有两个属性A[i]和B[i]。选择一些物品使得C*D最大化,其中C为所选物品中A[i]的不同的数字个数,D为所选物品中B[i]的总和。

                   

主要思路:由于A[i]比较小,所以可以对不同的A[i]进行一次dp,f[I]代表从所有A[i]==I的物品中选择一个子集使得权值和最大。g[I]代表选择了I个不同的A[i]所获得的最大权值。 g[I]=max(g[I],g[I-1]+f[J])。max(g[I]*I)就是答案。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

int f[200];

class AlienAndHamburgers{
public:
	int getNumber(vector <int> type, vector <int> taste){
		int n=type.size();
		for (int i=0; i<=100; ++i) f[i]=-200000;
		for (int i=0; i<n; ++i)
			f[type[i]]=max(f[type[i]],max(f[type[i]]+taste[i],taste[i]));
		sort(f,f+101);
		int ans=0,ans1=0;
		for (int i=100; i>=1; --i){
			ans1+=f[i];
			if (f[i]<-150000) break;
			ans=max(ans1*(101-i),ans);
		}  
		return ans;
	}
};


T2:

题目大意:给你{1,2....,2n}2n个数字,将它们分成两个有n个元素的几何。对于所有1<=i<=n,|A[i]-B[i]|>=K,A[i]为第一个组的第i小的元素,B[i]为第二组的第i小的元素。求有多少不同的方案。


主要思路:由于K<=10,所以可以考虑状压dp,f[i][j][opt]代表处理到第i个数字,小于i-K的数字有j个还没有分组,大于等于i-K且小于i的数字有opt状态还没分组的方案数。每次我们可以选择将第i个数进行分组或者忽略i。这样f求出来的方案必定是默认A[i]>=B[i],所以我们再定义g[i]:两个集合的前i个元素已经分好的方案数。g[i]=sigma(g[j]*f[(i-j)*2][0][0]),代表两个集合第j个元素的大小顺序和第j+1...i个元素的大小顺序不同,即 A[j]>B[j] -> A[k]<B[k],k=j+1...i。这样算出来的两个集合是不分顺序的,所以最终答案为g[n]*2。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

int f[101][101][1000];
int g[51];

const int P=1000000007;
int c[101][101];

int find(int x,int y){
	c[0][0]=1;
	for (int i=1; i<=x; ++i)
		for (int j=0; j<=min(i,y); ++j)
			c[i][j]=(j==0)?1:(c[i-1][j]+c[i-1][j-1])%P;
	return c[x][y];
}

class AlienAndSetDiv1{
public:
	int getNumber(int N, int K){
		if (K==1) return find(2*N,N);
		N*=2;
		memset(f,0,sizeof(f));
		f[0][0][0]=1;
		--K;
		int m=(1<<K)-1;
		for (int i=0; i<N; ++i)
			for (int j=0; j<=i; ++j)
				for (int k=0; k<=m; ++k)
					if (f[i][j][k]){
						int z=0,opt=((k<<1)+1) & m;
						if ((k & (1<<(K-1)))>0) ++z;
						f[i+1][j+z][opt]=(f[i+1][j+z][opt]+f[i][j][k])%P;
						opt=(k<<1) & m;
						if (j>0) 
							f[i+1][j+z-1][opt]=(f[i+1][j+z-1][opt]+f[i][j][k])%P;
					}
		memset(g,0,sizeof(g));
		g[0]=1;
		N/=2;
		for (int i=1; i<=N; ++i)
			for (int j=0; j<i; ++j)
				g[i]=(g[i]+(long long)g[j]*f[(i-j)*2][0][0] %P)%P;
		return g[N]*2%P;
	}
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值