您的位置:首页 > 其它

HFUT&AHU组团训练(一)----DP专题

2014-03-24 00:36 246 查看
网址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=42394#overview

密码:akak

A题:

题意:求两个字符串的最长公共子串,特别的是,这个子串的每块连续的部分必须大于等于题目所给的k。

思路:第一遍打时完全时一片浆糊,采用常规方法,各种细节没有考虑到,果断删了重打。既然段都要求>=k,那么我一次就先找那连续的长度k,再给这一段记录。相当于先局部,再整体。

#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

const int MAX_SIZE = 1010;

int K;
string s[2];
int v[MAX_SIZE][MAX_SIZE];

inline int common_len(int i, int j) {
for (int k = 0; k < MAX_SIZE; k++) {
if(i+k >= s[0].length() || j+k >= s[1].length()) return k;
if(s[0][i+k] != s[1][j+k]) return k;
}
return -1;
}

inline void check(int i, int j) {
const int len = common_len(i,j);
v[i+1][j+1] = max(v[i+1][j+1], max(v[i][j+1], v[i+1][j]));
if(len >= K) {
for (int k = K; k < len+1; k++)
v[i+k][j+k] = max(v[i+k][j+k], v[i][j] + k);
}
}

int main() {

for (int i = 0; i < MAX_SIZE; i++)
v[0][i] = v[i][0] = 0;
while(cin >> K) {
if(K == 0) break;
cin >> s[0];
cin >> s[1];
const int n = s[0].size(), m = s[1].size();

for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
v[i+1][j+1] = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
check(i,j);

cout << v
[m] << endl;
}
}


B题:

题意:有很多俄罗斯套娃,讲其恢复原装,求最后桌子上最少有几个套娃?

思路:先将宽度由小到达排序,再讲高度从大到小排序,为了保证同宽度的只能有一个存在在一组套娃。用一个数组维护高度,每个元素代表一组套娃,值为当前该组套娃高度,数组整体是递减的。每读取一个高度,去覆盖当前能覆盖最高套娃组,若没有能覆盖的,则新建一个元素,即增加一个套娃组。最后求数组长度。

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

struct aaa{
int w;
int h;
}a[40005];

int n;
int t;

int cmp(aaa a, aaa b){
if (a.w == b.w)
return b.h < a.h;
return b.w > a.w;
}

int d[40005];

int main(){
memset(d,1,sizeof(d));
cout << d[1] << endl;
scanf("%d",&t);
while(t--){
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d%d", &a[i].w ,&a[i].h);
sort(a, a+n, cmp);

int len = 0;
for (int i = 0; i < n; i++){
int l = 0, r = len;
int m;
while(l < r){
m = (l + r) >> 1;
if (a[i].h <= d[m])
l = m+1;
else r = m;
/*
if (a[i].h >= d[m])
r = m
else l = m;*/
}
d[l] = a[i].h;
len+=l==len;
}

printf("%d\n",len);
}
}


    

C题:

题意:有c(1<=c<=4)种颜色的牌,每种牌n(1<=n<=100)张。给出起始顺序,问最少移动几张牌,可使牌组有序,即牌从小到大,且同颜色为一组。

思路:关键在于c只有4种颜色,方便很多。枚举颜色出现的顺序,定一个序列,计算最长上升子序列s。用序列长度减去子序列长度,即是答案。

#include<iostream>
#include<string>
#include<queue>
#include<stack>
#include<cstdio>
#include<cmath>
using namespace std;

struct aaa{
int col;
int val;
}w[500];

int MAX = (1 << 31) -1;
int c, n, sum =MAX;
int d[500];
int a[5]={0};
bool v[500];
int q[500];

void card(){
int s = c*n;
for (int i = 1; i <= s; i++){
d[i] = w[i].val * a[w[i].col];
}

q[1] = d[1];
int len = 1;
for (int i = 2; i <= s; i++){
if (d[i] > q[len]) q[++len] = d[i];
else{
int l = 1; int r = len;
while(l <= r){
int m = (l + r) >> 1;
if (q[m] < d[i]) l = m + 1;
else r = m - 1;
}
q[l] = d[i];
}
}

if (sum > s - len) sum = s-len;
}
int main(){

scanf("%d%d", &c, &n);
int s = c*n;
for (int i = 1; i <= s; i++){
scanf("%d%d", &w[i].col, &w[i].val);
}
s = 1;
for (int i = 2; i <= c; i++)
s = s*i;

while(s--){
for (int i = 1; i <= c; i++){
a[i] = 1;
if (c > 1){
for (int j = 1; j <= c; j++)
if (i != j){
a[j] = 101;
if (c > 2){
for (int k = 1; k <= c; k++)
if (i!=k && j!= k){
a[k] = 20101;
if (c > 3){
for (int l = 1; l <= c; l++)
if (i != l && j != l && k!= l){
a[l] = 4010101;
card();
}
}else card();
}
}else card();
}
}else card();
}
}
printf("%d\n", sum);
}

D题:

题意:有一排药水,药水有自己的颜色(a,b...),每次混合相邻药水,会产生a*b的气体,位置不变,颜色变为(a+b) mod 100.最后求生成最小体积是多少

思路:用记忆化搜索即可,无注意点。

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

struct aaa{
int c;
int v;
};

aaa f[500][500];
int n;

aaa dp(int l, int r){
if (f[l][r].c < 100) return f[l][r];

aaa t,mag1,mag2;
t.v = (1 << 31) -1;
for (int i = l; i < r; i++){
mag1 = dp(l, i);
mag2 = dp(i+1, r);
// cout << l << " " << i << mag1.c << endl;
//cout << i+1 << " " << r << mag2.c << endl;
if (mag1.c*mag2.c+ f[l][i].v + f[i+1][r].v < t.v) {
t.v = mag1.c*mag2.c + f[l][i].v + f[i+1][r].v;
t.c = (mag1.c+mag2.c) %100;
}
}

f[l][r] = t;
return f[l][r];
}

int main(){

while(cin >> n){
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++){
f[i][j].c = 100;
f[i][j].v = 0;
}
for (int i = 1; i <= n; i++)
cin >> f[i][i].c;

aaa t = dp(1,n);
cout << t.v << endl;
}
}

F题:

题意:一帮人参加聚会,要求他们的美丽度和强壮度都必须同时满足大于任何一个人,或者小于任何一个人(不能及格比A强,一个比A弱)最多邀请多少人,并输出顺序(任意)

思路:最长上升序列变形,记录前驱节点即可。

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

struct aaa{
int s;
int b;
int p;
bool operator < (aaa a)const {
if (s == a.s) return b > a.b;
return s < a.s;
}
}a[100010];

int d[100010];
int pos[100010]={0};
int pre[100010]={0};

int main(){
int n;

scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d%d", &a[i].s, &a[i].b);
a[i].p = i;
}

sort(a+1,a+n+1);

int len = 1;
pos[1] = 1;
for (int i =2; i <= n; i++){
if (a[i].b > a[pos[len]].b){
pos[++len] = i;
pre[i] = pos[len-1];
}
else{
int l = 1; int r = len;
while(l <= r){
int m = (l +r) >> 1;
if (a[pos[m]].b < a[i].b) l = m + 1;
else r = m - 1;
}
pos[l] = i;
pre[i] = pos[l-1];
}
}

printf("%d\n", len);
for (int i = pos[len]; i; i = pre[i])
printf("%d ",a[i].p);
cout << endl;
}


I题:

题意:求一个点集最少点的数目,要求满足一棵树的任意一条边都至少有一个端点在这个点集中。

思路:开始想的二分图,结果发现很容易反正。最后使用树形dp,简单解决。

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

vector<int> f[100001];
int d[100001][2];
int n,x,y;

void treedp(int u, int rt){
d[u][0] = 0;
d[u][1] = 1;
for (int i = 0; i < f[u].size(); i++){
int temp = f[u][i];
if (f[u][i] == rt) continue;

treedp(temp, u);
d[u][0] += d[temp][1];
d[u][1] += min(d[temp][1], d[temp][0]);
}
}

int main(){
scanf("%d", &n);
for (int i = 0; i < n; i++)
f[i].clear();

for (int i = 1; i < n; i++){
scanf("%d%d", &x, &y);
f[x-1].push_back(y-1);
f[y-1].push_back(x-1);
}

treedp(0,-1);

cout << min(d[0][0], d[0][1]) << endl;
}

E题:

题意:潜水员需要OL升氧气和NL升氮气,有若干组气体瓶o升氧气,n升氮气,w重量。最后要求至少满足OL和NL的情况下,潜水员最轻要背多少?

思路:01背包变形,f[i][j][k]代表到第i组气体瓶时j氧气k氮气最少的重量。开始想可以用2维优化,但是发现因为状态转移方程式加法,无法改变。

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

int test,OL,NL,n,ol,on;
int f[1001][22][80];
struct aaa{
int o;
int n;
int w;
}a[1001];

int main(){
int MAX= (1 <<31) -1;
cin >> test;
while(test--){
cin >> OL >> NL;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].o >> a[i].n >> a[i].w;

for (int j = OL; j >= 0; j--)
for (int k = NL; k >= 0; k--){
f[0][j][k] = MAX;
}

for (int i = 1; i <= n; i++){
for (int j = OL; j >= 0; j--)
for (int k = NL; k >= 0; k--){
f[i][j][k] = f[i-1][j][k];
ol = j + a[i].o > OL ? OL : j + a[i].o;
on = k + a[i].n > NL ? NL : k + a[i].n;
if ( j == 0 && k == 0 && f[i][ol][on] > a[i].w)
f[i][ol][on] = a[i].w;
if (f[i-1][j][k] < MAX && f[i-1][j][k] + a[i].w < f[i][ol][on])
f[i][ol][on] = f[i-1][j][k] + a[i].w;
else f[i][j][k] = f[i-1][j][k];
}
}
cout << f
[OL][NL] <<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: