ACM-ICPC Live Archive 3222 Joke with Turtles
poj 2168 相同的题目
区间DP
题意:输入n,表示有n个海龟在一条直线上,乌龟可以站在相同的位置(即坐标可以相同),下面n行,每行两个数字,表示第i个乌龟给出的信息,第一个数字表示它前面有多少只乌龟,第二个数字表示它后面有多少个乌龟。并不是每个乌龟的信息都是正确,有些乌龟的信息是假的,或者和别的乌龟信息冲突,你的任务是选出尽量多的乌龟,使他们的信息不冲突,然后输出有多少个乌龟说谎,和那些乌龟的编号,可能有多种情况,只要保证说谎的乌龟数最少,输出哪种情况都可以
分析:乌龟可以站在一样的位置,我们给乌龟排名,可以把它们放在不同的位置 例如 1 2 3 3 3 4 4 5 , 虽然有些排名相同,但是放在不同位置,这样方便我们处理
有n个乌龟,所以我们要准备n个位置。对于一个乌龟,它前面和后面分别为a,b人,那么可以知道,它的位置一定在[a+1,n-b]内,至于它确切在哪个位置并不重要,因为这个区间内的乌龟排名相同的。这样我们就转化为,用一个区间来表示乌龟,这是非常重要的一步
然后没读入一个乌龟,我们就在那个区间计数,w[i][j]表示在这个区间内有多少个乌龟,如果乌龟数超过了(j-i+1)那么不能再计数,因为这个区间最多只容纳这些乌龟
想想我们的问题,是找出最少的说慌的乌龟,反过来就是可以共存的乌龟数最多,而乌龟用区间表示了,那不就是变成了选最多的区间?就是这样,而且要满足,选出来的区间不能重叠
关于“所选区间不能重叠”,这个东西不好解释,但,这却是一个显然的事实(往往越是显然的越难理解)。可以这样想,我们是允许排名相同的,但是我们已经给每只乌龟排在一个位置了,只允许是排名相同,不能是位置相同的,如果区间重叠,就变成了位置相同,这和我们最开始的定义是相矛盾的
这样问题变为,在总区间[1,n]里面,选一些区间,这些区间不重叠,而且每个区间有权值(表示这个区间有多少乌龟,这些乌龟,不就是排名相同的乌龟嘛,只是安排了不同位置,但位置一定在这个区间内),使得所选区间加起来权值最大,只是个典型的区间模型
dp[i]表示[1,i]区间的最大权值和,目标状态为dp[n],另外在dp过程中要记录路径
方程: dp[i] = max { dp[j] + w[j+1][i] }
记录路径是为了输出。
dp完了我们从n沿路径返回,没找到一个前驱,其实可以确定一个区间w[pre+1][now] , 这个区间对应的其实是乌龟,表示这些乌龟背选中了,它们信息不冲突,不说谎,把这些乌龟标记掉
这时候想想我们一开始为什么要给w[i][j]计数,其实就是记录里面的乌龟数,为什么w[i][j]>(j-i+1)后不能再计数,因为可以知道,肯定有一些乌龟是矛盾的,这个区间不能放下这么多乌龟
然后输出没有被选中的乌龟即可
程序跑了200ms左右,是因为在选乌龟的时候是直接暴力扫描的,没有做优化
有优化想法欢迎分享
#include <cstdio> #include <cstring> #include <vector> #include <utility> using namespace std; #define N 1010 int n; int w[N][N]; int dp[N] , p[N]; struct tur { int a,b,c; }t[N]; void solve() { memset(dp,0,sizeof(dp)); memset(p,-1,sizeof(p)); for(int i = 1; i <= n; i++) for(int j = 0; j<n; j++) if(dp[j] + w[j+1][i] > dp[i]) { dp[i] = dp[j] + w[j+1][i]; p[i] = j; } bool used[N]; memset(used,false,sizeof(used)); int pre,now,count,c; now = n; count = 0; while(1) //沿路径返回并标记说了真话的乌龟 { pre = p[now]; if(pre == -1) break; //区间为[pre+1,now] c = w[pre+1][now]; //有多少只重叠的乌龟 for(int i=0; i<n && c; i++) if(t[i].a == pre && t[i].b == n-now) { used[i] = true; c--; count++; } now = pre; } printf("%d",n-count); for(int i=0; i<n; i++) if(!used[i]) printf(" %d",i+1); printf("\n"); } int main() { while(scanf("%d",&n)!=EOF) { memset(w,0,sizeof(w)); for(int i=0; i<n; i++) { int a,b; scanf("%d%d",&a,&b); t[i].a = a; t[i].b = b; t[i].c = n - t[i].a - t[i].b; w[a+1][n-b]++; if(w[a+1][n-b] > t[i].c) w[a+1][n-b] = t[i].c; } solve(); } return 0; }