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;
}