zoukankan      html  css  js  c++  java
  • uva 经典习题选做(dp专项)

    都是一些从蓝皮书上找的题。

    1.UVA12983 The Battle of Chibi

    给你一个数字序列,让你求出长度为 (k) 的最长上升子序列的个数。((nleq 1000,Tleq 100)

    solution

    树状数组优化 (dp) .

    (f[i][j]) 表示以 第 (i) 个数为结尾,且最长上升子序列长度为 (j) 的数目。

    转移则有: (f[i][j] = displaystylesum_{k=1}^{i-1} f[k][j-1]) ((a[k] leq a[i])) .

    直接做的话复杂度是 (O(Tn^2)) .优化一下复杂度。

    考虑到转移只会发生在比 (a[i]) 小的数之间,所以考虑用权值树状数组优化一下。

    对每个 (k) 都开一个权值树状数组,转移时查询一下 (a[i]) 得前缀和即可。

    答案即为 (f[i][m]).

    复杂度 (O(Tnlogn)).

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int p = 1e9+7;
    const int N = 1010;
    int n,m,T,ans;
    int a[N],b[N],f[N][N],tr[N][N];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int lowbit(int x) { return x & -x;}
    void chenge(int x,int now,int val)
    {
    	for(; x <= n; x += lowbit(x)) tr[now][x] = (tr[now][x] + val) % p;
    }
    int query(int x,int now)
    {
    	int res = 0;
    	for(; x; x -= lowbit(x)) res = (res + tr[now][x]) % p;
    	return res;
    }
    int main()
    {
    	T = read();
    	for(int i = 1; i <= T; i++)
    	{
    		n = read(); m = read(); ans = 0;
    		memset(tr,0,sizeof(tr));
    		memset(f,0,sizeof(f));
    		for(int i = 1; i <= n; i++) a[i] = b[i] = read();
    		sort(b+1,b+n+1);
    		int num = unique(b+1,b+n+1)-b-1;
    		for(int i = 1; i <= n; i++) a[i] = lower_bound(b+1,b+num+1,a[i])-b;
    		for(int i = 1; i <= n; i++)
    		{
    			f[i][1] = 1; chenge(a[i],1,1);
    			for(int j = 2; j <= m; j++)
    			{
    				f[i][j] = query(a[i]-1,j-1);
    				chenge(a[i],j,f[i][j]);
    			}
    		}
    		for(int i = 1; i <= n; i++) ans = (ans + f[i][m]) % p;
    		printf("Case #%d: %d
    ",i,ans);
    	}
    	return 0;
    }
    

    2. UVA11464 Even Parity

    给你一个 (n∗n) 的 01 矩阵(就是每个元素只可能为 (0)(1) ),你的任务是把尽量少的 (0) 变成 (1) ,使得每个元素的上,下,左,右的元素(存在的情况下)之和均为偶数。 ((Tleq 30,nleq 15))

    solution

    状压 and 递推。

    一个很显然的暴力就是枚举每个矩阵最后的状态,然后判断一下每个解是否可行,复杂度 (O(T 2^{格子数})).

    一个比较重要的优化:当我们第一行的状态确定的时候,那么整个矩阵也就可以确定出来了。

    所以我们只需要枚举第一行的状态,然后判断是否符合条件即可,复杂度 (O(T2^n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int inf = 1e8;
    const int N = 50;
    int T,ans,n,s[N][N],b[N][N];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int calc(int x)
    {
    	int sum;
    	memset(b,0,sizeof(b));
    	for(int i = 1; i <= n; i++)
    	{
    		int tmp = (x>>(i-1)) & 1;
    		if(tmp == 1) b[1][i] = 1;
    		else if(s[1][i] == 1) return inf;//0不能变为1
    	}
    	for(int i = 2; i <= n; i++)
    	{
    		for(int j = 1; j <= n; j++)
    		{
    			sum = 0;
    			sum += b[i-2][j];//i-1,j 的上左右四个方位的和。
    			sum += b[i-1][j-1];
    			sum += b[i-1][j+1];
    			b[i][j] = sum % 2;//i-1,j 的下方的数
    			if(s[i][j] == 1 && b[i][j] == 0) return inf;//0不能变为1
    		}
    	}
    	int cnt = 0;
    	for(int i = 1; i <= n; i++)
    	{
    		for(int j = 1; j <= n; j++)
    		{
    			if(b[i][j] != s[i][j]) cnt++;//计算需要变化多少次。
    		}
    	}
    	return cnt;
    }
    int main()
    {
    	T = read();
    	for(int t = 1; t <= T; t++)
    	{
    		n = read(); ans = inf;
    		for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) s[i][j] = read();
    		for(int i = 0; i < (1<<n); i++)//枚举第一行的状态
    		{
    			ans = min(ans,calc(i));
    		}
    		if(ans == inf) printf("Case %d: %d
    ",t,-1);
    		else printf("Case %d: %d
    ",t,ans);
    	}
    	return 0;
    }
    

    3.UVA11825 Hackers' Crackdown

    我们把每个计算机以及和它相连的计算机看成一个集合 (p[i]) ,你需要将 (n) 个集合分成尽量多组,使得每一组里面所有集合的并集等于全集。((nleq 16))

    solution

    (n) 这么小铁定是状压 (dp).

    首先我们先预处理出 (p[i]) 数组。

    for(int i = 1; i <= n; i++)
    {
    	p[i] = (1<<(i-1));//它本身
    	x = read();
    	for(int j = 1; j <= x; j++)
    	{
    		int k = read() + 1;
    		p[i] |= (1<<(k-1));//与他相连的计算机
    	}
    }
    

    然后预处理出每个计算机二进制状态的并集 即 (zt[i]).

    (f[i]) 表示计算机二进制状态为 (i) (及选或不选) 的情况下,能分的组数最多是多少。

    转移则有 (f[i] = max(f[i],f[i) (xor) $ j]+1)$ ((zt[j] = 全集))

    枚举一下子集进行转移即可,复杂度大概为 (O(3^n)) 左右。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = (1<<17);
    int n,ans,step,x;
    int zt[N],f[N],p[20];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int main()
    {
    	while(1)
    	{
    		step++;
    		memset(p,0,sizeof(p));
    		memset(zt,0,sizeof(zt));
    		n = read(); ans = 0;
    		if(n == 0) break;
    		for(int i = 1; i <= n; i++)
    		{
    			p[i] = (1<<(i-1));
    			x = read();
    			for(int j = 1; j <= x; j++)
    			{
    				int k = read() + 1;
    				p[i] |= (1<<(k-1));
    			}
    		}
    		for(int i = 0; i < (1<<n); i++)
    		{
    			for(int j = 1; j <= n; j++)
    			{
    				if(i & (1<<(j-1))) zt[i] |= p[j];
    			}
    		}
    		f[0] = 0;
    		for(int i = 1; i < (1<<n); i++)
    		{
    			f[i] = 0;
    			for(int j = i; j; j = (j-1) & i)//枚举子集
    			{
    				if(zt[j] == (1<<n)-1) f[i] = max(f[i],f[i^j]+1);
                }
    			ans = max(ans,f[i]);
    		}
    		printf("Case %d: %d
    ",step,ans);
    	}
    	return 0;
    }
    

    咕咕咕。

    4.UVA10891 Game of Sum

    5.UVA10635 Prince and Princess

    6.UVA1169 Robotruck

    7.UVA1099 Sharing Chocolate

  • 相关阅读:
    devstack screen 详解
    devstack with neutron 参考文献
    Linux 启动文件、设置环境变量的位置
    Android解析服务器Json数据实例
    JDBC连接MySQL数据库的方法和实例
    Github如何更新远程代码
    最简单的Android教程之自定义控件
    Markdown入门教程
    如何将代码托管到GitHub上
    Github如何删除repository(仓库)
  • 原文地址:https://www.cnblogs.com/genshy/p/14056373.html
Copyright © 2011-2022 走看看