250:
题目大意:给定n(n <= 50)个物品,每个物品有两种属性ai和bi,现在要选出一个子集(可为空)。要求最大化Y * A,Y是选出的物品中ai的种类数,A为bi的总和。
基本思路:对每种ai做一次dp,求最大的bi的和,记为v[i]。然后再利用vi进行dp就好了。
#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;
}
};
450:
题目大意:
给定 1..2n 一共2n个数,现在让你将这些数填在一个2行n列的矩阵里,使得每行的数都是按列递增的,每列的数的差值要大于等于K。N<= 50, 1 <= K <= 10
基本思路:
设F[i][j][opt]代表填了1..i这些数字,且第1行填了j个,第2行填了 i – j个数,默认第一行多于第二行,第一行多的数且差值与i小于K的集合为opt的方案。
#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;
}
};
1000:
题目大意:
给定一个n(n <= 200)的排列,每次可以对这个序列做如下操作:
1. 选择非空的连续一段子序列
2. 将一段所有元素都替换成他们之中的最大值。
问操作至多K次后,一共有多少种不同的序列。基本思路:
很显然操作完后的序列为许多升序的段,所以设dp[i][j][k]代表处理完前i个位置了,且第i个位置填的数字为j,操作了至少K次的方案。有种比较暴力的方法是:枚举下一个位置的数字l,然后判断l是否能填在j后面,且能填在位置i,然后再进行转移即可。其实情况就三种,下个数字大于、等于或者小于当前数字,只要记个类似前缀和以及后缀和的东西即可。然后还有一些小细节要处理,最后的答案为sigma(i = 1 -> n)(j = 0 -> K)dp[n - 1][i][j]。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int dp[205][205][205];
int ll[205], rr[205], pos[205];
const int P = 1e9 + 7;
int n;
void updata(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
class AlienAndPermutation {
public:
int getNumber(vector <int> P, int K) {
n = P.size();
for (int i = 0; i < n; ++i) {
int x = P[i];
pos[x] = i;
ll[x] = rr[x] = i;
while (ll[x] - 1 >= 0 && P[ll[x] - 1] < x)
--ll[x];
while (rr[x] + 1 < n && P[rr[x] + 1] < x)
++rr[x];
}
for (int i = 1; i <= n; ++i)
if (ll[i] <= 0 && i != P[0])
dp[0][i][1] = 1;
dp[0][n + 1][0] = 1;
for (int i = 0; i + 1 < n; ++i)
for (int j = 0; j <= K; ++j) {
for (int k = 1; k <= n; ++k)
if (i + 1 >= ll[k] && i + 1 <= rr[k])
updata(dp[i + 1][k][j], dp[i][k][j]);
if (rr[P[i]] >= i + 1)
updata(dp[i + 1][P[i]][j + 1], dp[i][n + 1][j]);
for (int k = 1, res = 0; k <= n; ++k) {
if (ll[k] <= i + 1 && rr[k] >= i + 1 && pos[k] > i) {
if (k != P[i + 1])
updata(dp[i + 1][k][j + 1], res);
else updata(dp[i + 1][n + 1][j], res);
}
updata(res, dp[i][k][j]);
if (k == P[i]) updata(res, dp[i][n + 1][j]);
}
for (int k = n, res = 0; k > 0; --k) {
if (ll[k] <= i + 1 && rr[k] >= i + 1) {
if (k != P[i + 1]) updata(dp[i + 1][k][j + 1], res);
else updata(dp[i + 1][n + 1][j], res);
}
if (pos[k] <= i) {
updata(res, dp[i][k][j]);
if (k == P[i]) updata(res, dp[i][n + 1][j]);
}
}
}
int ans = 0;
for (int i = 0; i <= K; ++i)
for (int j = 1; j <= n + 1; ++j)
updata(ans, dp[n - 1][j][i]);
return ans;
}
};