SRM 605 T1 T2 T3

来源:互联网 发布:ubuntu设置无线网络 编辑:程序博客网 时间:2024/04/30 09:31

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;    }};


0 0
原创粉丝点击