好像最近写的几篇博客都是关于动态规划的,看来动态规划在acm里面也是常客~~~
hdu6249题目链接:点击打开链接
大意:给出m个闭区间,各区间左端点和右端点都在[1,n]内,求从中选出k个区间的最大区间并。
1≤T≤100
1≤K≤M
1≤N,M≤2000
1≤Li≤Ri≤N
数据规模见上,对其进行分析,估计这道题应该是用o(n^2)的解法
思路:对于区间问题,我首先想到的就是离散化处理,对各个区间端点进行排序标号,Java的话可以直接用Set和Map去做。当然对于各个区间也最好先排一下序方便处理,这里我是优先按右端点之后按左端点进行排序
然后预处理各个点,用prev[i]表示仅用一个区间,选i点,最左可以延伸到的点的序号(下面我结合样例说明)
对于第一个case,离散化三个点1 3 4,那么prev[3]=2(选第一个区间),prev[2]=1(选第三个区间),prev[1]=1(选第三个区间或第二个区间)
对于第二个case,离散化有四个点1 50 90 100,那么prev[4]=3,prev[3]=3(选第二个区间),prev[2]=1,prev[1]=1
之后就是进入dp部分了,这里要注意几个点:滚动数组(之前我多开了一维爆空间了),初始化值(因为我数组是直接最开始就开辟好空间的而不是输入一组测试数据开辟一次的,所以之前的结果可能对当前测试数据有影响,对于这道题,要适当注意赋0值。因为这个我也wa了一次)
for (int i=1;i<=cnt;i++)
{
g[i][0]=0;
g[i][1]=0;
}
for (int j=1;j<=k;j++)
{
for (int i=1;i<=cnt;i++)
{
f[i][0]=0;
f[i][1]=0;
if (prev[i]!=i+1)
{
f[i][0]=(short) (g[prev[i]][1]+(lisan[i]-lisan[prev[i]]+1));
f[i][1]=(short) (f[i][0]-1);
}
f[i][0]=getmax(f[i][0],f[i-1][0]);
f[i][1]=getmax(f[i][1],f[i-1][0]);
}
for (int i=1;i<=cnt;i++)
{
g[i][0]=f[i][0];
g[i][1]=f[i][1];
}
}
System.out.println(f[cnt][0]);
这里我滚动掉了一维j,实际上应该是f[i][j][0]和f[i][j][1],表示最右端为i点(可取/不可取),最多能选j个区间的最大区间并。那么考虑两个策略
一个是取i点所在的区间,此时价值
w1=f[prev[i]][j-1][1]+(lisan[i]-lisan[prev[i]])(没考虑右端点) w2=1或者0(右端点可取还是不可取)
w=w1+w2
注:这里prev数组就体现出它的价值了,因为选包含i点的一个区间的话肯定是越往左延伸,总共占有的区间越大,越划算
另一个则是放弃i点所在区间,选之前的区间,那么
w=f[i-1][j][0]
两个策略里面选最优的那个即可
然后发现这里只用到了j和j-1,所以能滚动掉这一维
最后结果就是f[cnt][0]
全部代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
class Reader{
static BufferedReader reader;
static StringTokenizer tokenizer;
static void init(InputStream input)
{
reader=new BufferedReader(new InputStreamReader(input));
tokenizer=new StringTokenizer("");
}
static String next() throws IOException{
while (!tokenizer.hasMoreTokens())
tokenizer=new StringTokenizer(reader.readLine());
return tokenizer.nextToken();
}
static int nextInt() throws IOException{
return Integer.parseInt(next());
}
}
class Mset {
short l,r;
/**
* @param l
* @param r
*/
public Mset(short l, short r) {
super();
this.l = l;
this.r = r;
}
/**
*
*/
public Mset() {
super();
// TODO Auto-generated constructor stub
}
}
public class Main {
static int t,n,m,k,l,r;
static short cnt;
static Mset mset[];
static short lnum[],rnum[],prev[],lisan[];
static short f[][],g[][];
static TreeSet treeSet;
static TreeMap treeMap;
static Comparator cmp=new Comparator() {
@Override
public int compare(Mset o1, Mset o2) {
// TODO Auto-generated method stub
if (o1.rb?a:b;
}
static short getmin(short a,short b)
{
return a=1;j--)
if (rnum[j]>=i)
prev[i]=getmin(prev[i],lnum[j]);
else
break;
}
for (int i=1;i<=cnt;i++)
{
g[i][0]=0;
g[i][1]=0;
}
for (int j=1;j<=k;j++)
{
for (int i=1;i<=cnt;i++)
{
f[i][0]=0;
f[i][1]=0;
if (prev[i]!=i+1)
{
f[i][0]=(short) (g[prev[i]][1]+(lisan[i]-lisan[prev[i]]+1));
f[i][1]=(short) (f[i][0]-1);
}
f[i][0]=getmax(f[i][0],f[i-1][0]);
f[i][1]=getmax(f[i][1],f[i-1][0]);
}
for (int i=1;i<=cnt;i++)
{
g[i][0]=f[i][0];
g[i][1]=f[i][1];
}
}
System.out.println(f[cnt][0]);
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Reader.init(System.in);
t=Reader.nextInt();
mset=new Mset[2001];
lnum=new short[2001];
rnum=new short[2001];
lisan=new short[4001];
f=new short[2001][2];
g=new short[2001][2];
for (int i=1;i<=2000;i++)
mset[i]=new Mset();
for (int casenum=1;casenum<=t;casenum++)
{
n=Reader.nextInt();
m=Reader.nextInt();
k=Reader.nextInt();
treeSet=new TreeSet();
treeMap=new TreeMap();
for (int i=1;i<=m;i++)
{
l=Reader.nextInt();
r=Reader.nextInt();
mset[i].l=(short)l;
mset[i].r=(short)r;
treeSet.add((short)l);
treeSet.add((short)r);
}
System.out.print("Case #"+casenum+": ");
Arrays.sort(mset,1,m+1,cmp);
cnt=0;
for (short num:treeSet) {
treeMap.put(num,++cnt);
lisan[cnt]=num;
}
deal();
}
}
}