http://acm.timus.ru/problem.aspx?space=1&num=1651
题意: 给出一个有向图的chain,求关于这个chain的shortest subchain(起点、终点与原chain相同&边出现的顺序与原chain相同)。
Idea:
一
DP :
依次处理每个a[i] : f[a[i]]=f[a[i-1]] + 1;
个人感觉这道题,难不在DP方程,而是想到用DP来做这道题。当时我看到这道题,第一反应就是图论,尝试用缩点的方法,再然后就放弃了。
小插曲:
这道题的路径输出也卡了我好久,最初我的思路是去记录 f[i] 对应的上一个结点j ,
f[i] = f[j] + 1 ,pre[i] = j; 然后一直WA,想不清楚。
对比了下正确的程序,只是在记录路径上的不同,正确的程序记录的是
f[i] = f[j] + 1, pre[i的位置] = j所在的位置 ,最后通过a[N]的位置,依次向前找。
然后还是一直想不清楚,觉得没有什么区别,举出反例。今天早上突然就闪过一个想法:假设答案是 KàN,N是最终结点且在中间的位置mid,假如在mid之后K再次被更新。。。。。快一个星期了,终于又答案了。(KEY:分类讨论、假设-验证)
反例: 1,2,3,4,9,10,2,9,7,8,10
正确答案:1,2,3,4,9,10; 错误:1,2,9,10
二、最短路
搜题解的时候发现有最短路的解法,具体忘了。
#include <cstdio> #include <iostream> #include <fstream> #include <cstring> #include <string> #define OP(s) cout<<#s<<"="<<s<<" "; #define TP(i,j,k) cout<<#i<<"="<<i<<" "<<#j<<"="<<j<<" "<<#k<<"="<<k<<endl; #define PP(s) cout<<#s<<"="<<s<<endl; using namespace std; int main() { freopen("test.txt","r",stdin); int N; while (~scanf("%d",&N)) { static int a[100010]; for (int i = 1; i <= N; i++) scanf("%d",a+i); static int f[10010],pre[100010],pos[10010]; for (int i = 0; i < 10010; i++) f[i] = 1<<29; memset(pre,0,sizeof(pre)); f[a[1]] = 0; pos[a[1]] = 1; for (int i = 2; i <= N; i++) { if (f[a[i-1]] + 1 < f[a[i]]) { f[a[i]] = f[a[i-1]] + 1 ; pre[i] = pos[a[i-1]]; pos[a[i]] = i; } } int p = pos[a[N]]; static int ans[10010]; int sa = 0; while (p != 1) { ans[++sa] = a[p]; p = pre[p]; //if (p == a[1]) break; } printf("%d",a[1]); for (int i = sa; i > 0; i--) printf(" %d",ans[i]); puts(""); } return 0; }