您的位置:首页 > 其它

动态规划部分题目小结

2015-11-28 22:39 447 查看
UVA10635 LCS转LIS
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19053
两个长度为p+1和q+1的序列A,B,序列中元素互不相同,且都是1到n^2的整数,求AB的LCS,将A中元素变为1到p+1,B中不在A中的元素直接删掉,问题转换为在B中求LIS

复杂度nlogn。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 255*255;
const int INF = 0x3f3f3f3f;
int f[maxn],g[maxn],pos[maxn];
int LIS(int n)
{
for(int i = 1; i <= n; ++i) g[i] = INF;
int ans = 0;
for(int i = 1; i <= n; ++i) {
int k = lower_bound(g+1,g+1+n,f[i]) - g;
ans = max(ans,k);
g[k] = f[i];
}
return ans;
}
int main()
{
int T;scanf("%d",&T);
for(int cas = 1; cas <= T; ++cas) {
int n,p,q;
scanf("%d%d%d",&n,&p,&q);
memset(pos,0,sizeof(*pos)*(n*n+4));
for(int i = 0; i <= p; ++i) {
int x;
scanf("%d",&x);
pos[x] = i+1;
}
int idx = 0;
for(int i = 0; i <= q; ++i) {
int x;scanf("%d",&x);
if(pos[x]) {
f[++idx] = pos[x];
}
}
//for(int i = 1; i <= idx; ++i) printf("%d%c",f[i]," \n"[i==idx]);
int ans = LIS(idx);
printf("Case %d: %d\n",cas,ans);
}
return 0;
}
UVA 10891 区间型dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19309
一个数组,AB轮流取数,只能取连续的,只能从两端取,A先取,都采取最优策略,得分为取的数的和,求A得分-B得分。令d[i][j]表示先手取区间[i,j]能得到的最大价值,转移为

d[i,j] = min(sum[i,j] - min(d[i,k]),sum[i,j] - min(d[k,j]))可以用辅助数组加速转移。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn],f[maxn][maxn],g[maxn][maxn];
int s[maxn],a[maxn];
int main()
{
int n;
while(scanf("%d",&n) == 1 && n) {
s[0] = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d",a+i);
dp[i][i] = a[i];
f[i][i] = a[i];
g[i][i] = a[i];
s[i] = s[i-1] + a[i];
}
for(int L = 1; L <= n; ++L) {
for(int i = 1; i + L <= n; ++i) {
int j = i + L;
int m = 0;
m = min(m,f[i+1][j]);
m = min(m,g[i][j-1]);
dp[i][j] = s[j] - s[i-1] - m;
f[i][j] = min(dp[i][j],f[i+1][j]);
g[i][j] = min(dp[i][j],g[i][j-1]);
}
}
printf("%d\n",-s
+(dp[1]
<<1));
}
return 0;
}

uva11825 状压dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=20243
把p1,p2,p3,...pn,n个集合分成尽量多的组,使得组内集合的并为全集,状压dp可做,注意枚举子集的技巧。令d[s]表示s最多能分多少组,转移为d[s] = max(d[s],d[s^s0]+1)需保证s0里集合并集为全集。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 16;
int d[1<<maxn],p[maxn],cover[1<<maxn];
void init(int n)
{
for(int i = 0; i < n; ++i) {
p[i] = (1<<i);
int cnt;scanf("%d",&cnt);
for(;cnt;--cnt) {
int x;scanf("%d",&x);
p[i] |= (1<<x);
}
}
for(int s = 0; s < 1<<n; ++s) {
cover[s] = 0;
for(int b = 0; b < n; ++b) {
if((s>>b)&1) cover[s] |= p[b];
}
}
}
int main()
{
int n,cas = 0;
while(scanf("%d",&n)==1&&n) {
init(n);
memset(d,0,sizeof d);
int ALL = (1<<n)-1;
for(int s = 1; s <= ALL; ++s) {
for(int ns = s; ns; ns = (ns-1)&s) {
if(cover[ns] == ALL) d[s] = max(d[s],d[s^ns]+1);
}
}
printf("Case %d: %d\n",++cas,d[ALL]);
}
return 0;
}

UVA 10859 树形dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19277
给一个n点,m条边的森林,选最少的点,使得所有的边都被覆盖,且被两个点覆盖的边尽可能的多,原问题等价于选最少的点和使最少的边只被一个点覆盖,令w = M*v1 + v2,v1是点数,v2是被一个点被覆盖的边,只要保证M  > (maxv2-minv2)就能保证在v1最优的情况下在去优化v2。d[u][x]表示u为根的子树w的最小值,x表示u的父亲是否选择。选u的时候d[u][x] =M+sum(d[v][1])+test(u不是根),不选u,d[u][x]
= sum(d[v][0]) + x,(不选u只有当u使根或者x等于1才可行)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
const int M = 2000;
vector<int> G[maxn];
int d[maxn][2];
int dp(int u,int x,int fa)
{
int & ans = d[u][x];
if(ans != -1) return ans;
ans = M;///u处放灯总是和法的
for(vector<int>::size_type i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v != fa) ans +=dp(v,1,u);
}
if(fa != -1 && x == 0) ++ans;///u不是根,且父亲没放灯
if(fa==-1 || x == 1) {///u是根,或者u的父亲已放灯,u可以不放灯
int sum = 0;
for(vector<int>::size_type i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v != fa) sum += dp(v,0,u);
}
sum += x;
ans = min(ans,sum);
}
return ans;
}
int main()
{
int T;scanf("%d",&T);
while(T--) {
int n,m;scanf("%d%d",&n,&m);
for(int i = 0; i <= n; ++i) G[i].clear();
memset(d,-1,sizeof d);
for(int i = 0; i < m; ++i) {
int u,v;scanf("%d%d",&u,&v);
++u,++v;
G[u].push_back(v);
G[v].push_back(u);
}
int ans = 0;
for(int i = 1; i <= n; ++i) {
if(d[i][0]==-1) ans += dp(i,0,-1);
}
printf("%d %d %d\n",ans/M,m-ans%M,ans % M);
}
return 0;
}

LA3983 单调队列优化dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=10159
n个垃圾,第i个坐标为(xi,yi,wi),wi为重量,机器人(初始在原点)按照编号从小到大的顺序将n个垃圾放到原点的垃圾桶处,每次可以选择一些垃圾放会垃圾桶,但总重量不得超过c,求机器人行走的最短距离,行走距离为曼哈顿距离。

令d[i] 表示将前i个垃圾放回原点的最小距离,则d[i] = min(d[j] + dist[j+1]+w[j+1,i]+dist[i]),复杂度为n^2,w[i,j]表示从i走到i+1,...j的曼哈顿距离,可以用单调队列优化一下,用sum[i]-sum[j]表示w[j,i],则转移变为d[i] = min(d[j]+dist[j+1]-w[j+1]) + dist[i]+w[i],单调队列维护d[j]+dist[j+1]-w[j+1]的最小值即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int x[maxn],y[maxn];
int total_dist[maxn],total_weight[maxn],dist2orgin[maxn];
int d[maxn];
int func(int i)
{
return d[i] - total_dist[i+1] + dist2orgin[i+1];
}
int main()
{
int T;scanf("%d",&T);
while(T--) {
int n,c;scanf("%d%d",&c,&n);
total_dist[0] = total_weight[0] = 0;
x[0] = y[0] = 0;
for(int i = 1; i <= n; ++i) {
int w;
scanf("%d%d%d",x+i,y+i,&w);
dist2orgin[i] = abs(x[i]) + abs(y[i]);
total_dist[i] = total_dist[i-1] + abs(x[i]-x[i-1]) + abs(y[i]-y[i-1]);
total_weight[i] = total_weight[i-1] + w;
}
deque<int>Q;
Q.push_front(0);
for(int i = 1; i <= n; ++i) {
while(!Q.empty() && total_weight[i] - total_weight[Q.front()] > c)Q.pop_front();
d[i] = func(Q.front()) + total_dist[i] + dist2orgin[i];
while(!Q.empty() && func(i) <= func(Q.back())) Q.pop_back();
Q.push_back(i);
}
printf("%d\n",d
);
if(T)puts("");
}
return 0;
}

LA4794 状压dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=11676
长宽为x,y的矩形能否划分成面积为a1,a2,...an的小矩形,状压dp,f[S][r][c]表示r×c的矩形能否划分成S集合的一些小矩形,限制r<=c,可以将状态表示成f[S][r]。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 16;
const int maxw = 100 + 4;
int sum[1<<maxn],f[1<<maxn][maxw];
int a[maxn];
const char *str[] = {"No","Yes"};
int bitcount(int S)
{
int tot = 0;
for(; S ;S = (S-1) & S) ++tot;
return tot;
}
int dp(int S,int x)
{
int & ans = f[S][x];
if(ans + 1) return ans;
ans = 0;
if(bitcount(S) == 1) return ans = 1;
int y = sum[S]/x;
for(int S0 = (S-1)&S; S0; S0 = (S0-1)&S) {
int S1 = S ^ S0;
if(sum[S0]%x==0&&dp(S0,min(x,sum[S0]/x))&&dp(S1,min(x,sum[S1]/x))) return ans = 1;
if(sum[S0]%y==0&&dp(S0,min(y,sum[S0]/y))&&dp(S1,min(y,sum[S1]/y))) return ans = 1;
}
return ans = 0;
}
int main()
{
int n;
for(int cas = 1; ; ++cas) {
scanf("%d",&n);
if(!n) return 0;
int x,y;scanf("%d%d",&x,&y);
for(int i = 0; i < n; ++i) {
scanf("%d",a + i);
}
int ALL = (1<<n) - 1;
for(int s = 0; s <= ALL; ++s) {
sum[s] = 0;
for(int i = 0; i < n; ++i) {
if((s>>i)&1) sum[s] += a[i];
}
}
int ans = 0;
if(sum[ALL] == x*y && sum[ALL] % x == 0) {
memset(f,-1,sizeof f);
ans = dp(ALL,min(x,y));
}
printf("Case %d: %s\n",cas,str[ans]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息