这次的比赛。。被虐爆了。。做了一个多小时确定下来除了第一题我都做不出来之后。。。我就。。。就。。。。。
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4510
题解:水题,格式要注意,还需要注意的是时钟。。。一圈12小时。。不是24小时。。。
#include <iostream> using namespace std; int main() { int n; scanf("%d", &n); while (n--) { int a, b, c, d, e, f; scanf("%d:%d:%d %d:%d:%d", &a, &b, &c, &d, &e, &f); int xt = a * 3600 + b * 60 + c; int yt = d * 3600 + e * 60 + f; a %= 12; d%= 12; int xtime = a * 3600 + b * 60 + c; int ytime = d * 3600 + e * 60 + f; while (xtime < ytime) xtime += 43200; int cha = xtime - ytime; printf("%02d:%02d:%02d\n", cha/3600, (cha%3600)/60, (cha%3600)%60); } return 0; }
连接:http://acm.hdu.edu.cn/showproblem.php?pid=4511
题解:谁会啊。。最后了全场也就5个AC的有木有!!!
一下转自大牛(http://hi.baidu.com/chenwenwen0210/item/ca0768d039b0f1d793a974c9)
解题报告:这题初看没什么头续,看到了这些路径,然后又想看的数据范围,K很好,想到了Trie,然后又想到了字符串匹配,于是想到了AC自动机。
想到了AC自动机,这题就好做了,就是一个AC自动机DP嘛。
把题目给的非法路径构成一棵Trie树,然做跑一次AC自动机。
然后做DP
dp[i][j]代表在第i个点自动机状态在j的最小距离。
然后枚举可走的点去转移就行了。
自动机状态最多有5*100个。
总的DP状态有50*500,每一个转移是50,最后的复杂度是50*50*500
刚刚过题目。
PS:一次AC,很高兴。
#include<stdio.h> #include<math.h> #include<string.h> const int MAX=1005; const int MAXSON=50; struct { int id,next[MAXSON],fail; }node[1000000]; int n,tot; char mod[1000005]; int len[MAX]; int q[1000000]; void clr() { int i; tot++; for(i=0;i<MAXSON;i++) node[tot].next[i]=0; node[tot].id=node[tot].fail=0; } void ins() { int tmp,i,n; int h=0; scanf("%d",&n); while(n--) { scanf("%d",&tmp); tmp--; if(node[h].next[tmp]==0) { clr(); node[h].next[tmp]=tot; } h=node[h].next[tmp]; } node[h].id++; } void get_fail() { int h=0,i; int f=-1,r=0; int tmp,fail,k; q[0]=0; while(f!=r) { tmp=q[++f]; if(node[node[tmp].fail].id>0)//自动机添加一个往前走的东西。错在这里啊,好久没有用AC自动机了 node[tmp].id=1; for(i=0;i<MAXSON;i++) { if(node[tmp].next[i]==0) { fail=node[tmp].fail; node[tmp].next[i]=node[fail].next[i]; continue; } k=node[tmp].next[i]; fail=node[tmp].fail; if(node[fail].next[i]!=k) node[k].fail=node[fail].next[i]; else node[k].fail=0; q[++r]=k; } } } const double EPS=1.0e-8; bool dblcmp(double x) { if(fabs(x)<EPS)return 0; return x<0?-1:1; } struct Point { double x,y; }p[55]; double disPP(Point a,Point b) { double dx=a.x-b.x; double dy=a.y-b.y; return sqrt(dx*dx+dy*dy); } const double INF=1000000000.0*100000000.0; double dp[55][5*110]; int main() { int n,m; int i,j,k; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0)break; for(i=1;i<=n;i++) { scanf("%lf%lf",&p[i].x,&p[i].y); } tot=-1; clr(); while(m--) { ins(); } get_fail(); for(i=0;i<=n;i++) { for(j=0;j<=tot;j++) { dp[i][j]=INF; } } int h=node[0].next[0]; if(node[h].id>0) { puts("Can not be reached!"); continue; } dp[1][h]=0; double cost=0; for(i=1;i<=n;i++) { for(j=0;j<=tot;j++) { if(dblcmp(dp[i][j]-INF)==0)continue; for(k=i+1;k<=n;k++) { h=node[j].next[k-1]; if(node[h].id)continue; cost=disPP(p[i],p[k])+dp[i][j]; if(cost<dp[k][h])dp[k][h]=cost; } } } double ans=INF; for(i=0;i<=tot;i++) { if(dp[n][i]<ans)ans=dp[n][i]; } if(dblcmp(INF-ans)!=0)printf("%.2f\n",ans); else puts("Can not be reached!"); } return 0; }
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4512
题解:(转自大牛http://www.cnblogs.com/fzf123/archive/2013/03/23/2976903.html)
做法:枚举断点,分成两个段,求最长公共上升子序列。
/* Author:Zhaofa Fang Lang:C++ */ #include <cstdio> #include <cstdlib> #include <sstream> #include <iostream> #include <cmath> #include <cstring> #include <algorithm> #include <string> #include <utility> #include <vector> #include <queue> #include <stack> #include <map> #include <set> using namespace std; typedef long long ll; #define DEBUG(x) cout<< #x << ':' << x << endl #define REP(i,n) for(int i=0;i < (n);i++) #define REPD(i,n) for(int i=(n-1);i >= 0;i--) #define FOR(i,s,t) for(int i = (s);i <= (t);i++) #define FORD(i,s,t) for(int i = (s);i >= (t);i--) #define PII pair<int,int> #define PB push_back #define MP make_pair #define ft first #define sd second #define lowbit(x) (x&(-x)) #define INF (1<<30) int dp[205],h[205]; int f[205][205]; int a[205],b[205]; int calc(int n,int m){ memset(dp,0,sizeof(dp)); FOR(i,1,n){ int k = 0; FOR(kk,1,m)f[i][kk] = f[i-1][kk]; FOR(j,1,m){ if(b[j]<a[i] && dp[k]<dp[j])k = j; if(b[j]==a[i]&&dp[k]+1>dp[j]){ dp[j] = dp[k] + 1; f[i][j] = i*(m+1)+k; } } } int ma = 1; FOR(j,1,m) if(dp[ma]<dp[j])ma = j; return dp[ma]; } int main() { //freopen("in","r",stdin); int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); FOR(i,1,n)scanf("%d",&h[i]); int ans = -1; FOR(i,1,n){ int tmp1,tmp2; FOR(j,1,i)a[j] = h[j]; FORD(j,n,i+1)b[n+1-j] = h[j]; tmp1 = calc(i,n-i); b[n-i+1] = h[i]; tmp2 = calc(i,n-i+1); if(tmp2>tmp1)ans = max(ans,tmp1*2+1); else ans = max(ans,tmp1*2); } printf("%d\n",ans); } return 0; }
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4513
题解:(转自:http://hi.baidu.com/chenwenwen0210/item/51b72039793833f56d15e9ba)
解题报告:这题其实是求一个最长的回文子串,这儿和以前不一样的是要求是先上升后下降。
我们可以通过Manacher算法改造过来
下面是学习资料。
http://hi.baidu.com/chenwenwen0210/item/482c84396476f0e02f8ec230
和普通回文串不一样的地方就是每一扩展的时候要判断一下是不是<=前面的值。
具体改造见代码。
#include<stdio.h> #include<math.h> #include<string.h> #include<map> #include<algorithm> #include<queue> using namespace std; typedef __int64 lld; const int MAX=110000*2; int str[MAX];//原字符串 int sb[MAX]; int p[MAX];//表示以i为中心的回文半径, /*p[i]-1刚好是原字符串以第i个为中心的回文串长度。 画画图就知道了,因为两端配匹的肯定是字符g */ /* Mancher主算法。 学习地址:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824 功能:求出以i为中心的回文半径p[i]; 参数:传入构造好的字符串长度 特殊说明:因为前面加了一个无效字符,所以下标从1开始。 例题:http://acm.hdu.edu.cn/showproblem.php?pid=3068 http://poj.org/problem?id=3974 http://acmpj.zstu.edu.cn/JudgeOnline/showproblem?problem_id=3780 http://acmpj.zstu.edu.cn/JudgeOnline/showproblem?problem_id=3769 http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=12581#problem/A http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3661 http://acm.hdu.edu.cn/showproblem.php?pid=3948 */ bool dig(char x){return x>='0'&&x<='9';} int getval() { int ret=0,sign=1; char c; while(!dig(c=getchar())&&c!='-'); if(c=='-')sign=-1; else ret=c-'0'; while(dig(c=getchar()))ret=ret*10+c-'0'; return ret*sign; } void Manacher(int n) { int i; int mx=0;//记录前面回文串最长影响到的地方。不一定是最长回文串造成的。 int id;//最长影响串的ID; p[0]=0; for(i=1;i<n;i++) { p[i]=1;//至少是1 if(mx>i)//i受到影响即,id+p[id]-1>=i; { p[i]=p[2*id-i];//2*id-i是i关于id的对称点相当于是id-(i-id); if(mx-i<p[i])p[i]=mx-i; //由于对称点的回文半径可能超过mx-i,因为mx后面的还没有配过 //所以要取小的。等等继续配 } //向两端配匹 while(str[i-p[i]]==str[i+p[i]]) { if(str[i+p[i]]==1) { p[i]++; } else { int a=i-p[i]; int b=i+p[i]; if(a+2<=b-2&&str[a]<=str[a+2]&&str[b-2]>=str[b]) { p[i]++; continue; } else if(a==b) { p[i]++; continue; } else if(a+2>b-2) { p[i]++; continue; } break; } } if(i+p[i]>mx) { mx=i+p[i]; id=i; } } } /* 功能:构造字符串,在每一个字符前插入一个,g,一般用'#' 第一个字符前面再插入,first,一般用'$' 最后再插入g字符 总之不是在输入中出现的字符就行了。 比如abb,构造成$#a#b#b# 参数:<first,第一个字符>,<g,一般字符> 返回值:构造好的字符串长度 */ int pre(int first,int g,int m) { int i,n=0; memcpy(sb,str,sizeof(int)*(m+2)); str[0]=first; n++; for(i=0;i<m;i++) { str[n++]=g; str[n++]=sb[i]; } str[n++]=g; str[n]=3; return n; } int main() { int n; int i; int CS=1; int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;i++) { //scanf("%d",&str[i]); str[i]=getval(); } n=pre(0,1,n); Manacher(n); int ans=0; for(i=1;i<n;i++) { if(p[i]>ans)ans=p[i]; } printf("%d\n",ans-1); } return 0; }另一位大牛的: http://www.cnblogs.com/aigoruan/archive/2013/03/22/2976538.html
代码就不贴了。。。
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514
题解:好久没做图论了。。这次看到傻眼了。。。于是当时就没做。。第二天起床之后发现原来没那么难。。。。简单一个并查集就行了。。我狂晕。。。最好一次机会让我给错过了。。以后比赛坚决不能放弃了。。。。。
#include <iostream> using namespace std; #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) const int maxn = 1000005; int fa[maxn], len[maxn]; int n, m; int find(int x) { if (x == fa[x]) return x; return find(fa[x]); } int main() { while (scanf("%d %d", &n, &m) != EOF) { int i; bool flag = false; for (i=0; i <= n; i++) fa[i] = i; memset(len, 0, (n+1)*sizeof(len[0])); for (i=0; i<m; i++) { int a, b, length; scanf("%d %d %d", &a, &b, &length); int faa = find(a); int fab = find(b); if (faa == fab) flag = true; else if (!flag) //剪枝 { int c = min(faa,fab); int d = max(faa,fab); fa[d] = c; len[c] += length; } } if (flag) puts("YES"); else { int maxlen = 0; for (i=1; i<=n; i++) if (len[i] > maxlen) maxlen = len[i]; printf("%d\n", maxlen); } } return 0; }