1004 hdu 4334 http://acm.hdu.edu.cn/showproblem.php?pid=4334
题意:
给定五个集合,从五个集合中分别取出5个数,如果存在满足a1 + a2 + a3 + a4 +a5 == 0 输出yes,否则no; 集合的大小小于200 ai的取值为 [-10^15, 1 0^15]
思路:
开始的时候O(n^2)求出前两个集合的所有可能的和,然后O(n^3)+二分log(10^4)做,结果竟然tle。。坑爹啊。。。。最后用了hash过的。好像poj有个类似的题目,也是hash做的。
hash开散列两种实现方式,
挂链:
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <ctime> #include <queue> #include <map> #include <sstream> #define maxn 177777 #define ll __int64 using namespace std; ll num[6][205]; struct node { ll num; node *next; }*p[maxn + 10],head[40015]; int main() { //freopen("d.txt", "r", stdin); int t,i,j,k; int n; scanf("%d",&t); while (t--) { memset(p,0,sizeof(p)); scanf("%d",&n); for (i = 0; i < 5; ++i) { for (j = 0; j < n; ++j) scanf("%I64d",&num[i][j]); } int pos = 0; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { ll tmp = num[0][i] + num[1][j]; int tp = tmp%maxn; if (tp < 0) tp = -tp; node *q = &head[pos++]; q->num = tmp; q->next = p[tp]; p[tp] = q; } } bool flag = true; for (i = 0; i < n && flag; ++i) { for (j = 0; j < n && flag; ++j) { for (k = 0; k < n && flag; ++k) { ll tmp = (num[2][i] + num[3][j] + num[4][k])*(-1.0); int tp = tmp%maxn; if (tp < 0) tp = -tp; node *q; for (q = p[tp]; q != NULL; q = q->next) { if (q->num == tmp) { flag = false; break; } } } } } if (!flag) puts("Yes"); else puts("No"); } return 0; }
vector:
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <vector> #define maxn 17777 #define ll __int64 using namespace std; vector<ll>vc[maxn]; ll num[6][205]; int main() { //freopen("d.txt","r",stdin); int t,i,j,n,k; scanf("%d",&t); while (t--) { scanf("%d",&n); for (i = 0; i < 5; ++i) { for (j = 0; j < n; ++j) scanf("%I64d",&num[i][j]); } for (i = 0; i < maxn; ++i) vc[i].clear(); for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { ll tmp = num[0][i] + num[1][j]; int tp = tmp%maxn; if (tp < 0) tp = -tp; vc[tp].push_back(tmp); } } bool flag = true; for (i = 0; i < n && flag; ++i) { for (j = 0; j < n && flag; ++j) { for (k = 0; k < n && flag; ++k) { ll tmp = (-1.0)*(num[2][i] + num[3][j] + num[4][k]); int tp = tmp%maxn; if (tp < 0) tp = - tp; int sz = vc[tp].size(); for (int ki = 0; ki < sz; ++ki) { if (vc[tp][ki] == tmp) { flag = false; break; } } } } } if (!flag) puts("Yes"); else puts("No"); } return 0; }
1006 hdu 4336 http://acm.hdu.edu.cn/showproblem.php?pid=4336
题意:
给出n种不同卡片在买的小吃力里面出现的可能,求凑齐n种卡片要买的小吃的平均数量。
思路:
根据官方解题报告做的:
设卡片的分布p=(p1,p2,...,pn),T(p)表示拿到所有卡片时买的零食数目,有
由容斥原理得,
由于我没坐过状态压缩的题目,所以这里的代码不大好理解。
#include <cstdio> #include <iostream> using namespace std; const int maxn = 22; double p[maxn]; int main() { int n,i,j; while (~scanf("%d",&n)) { for (i = 0; i < n; ++i) scanf("%lf",&p[i]); double ans = 0.0; //根据二项式定理C(n,0)+C(n,1) + ... + C(n,n) = 2^n //所以这里2^n - 1种可能,枚举 for (i = 1; i < (1<<n); ++i) { int ct = 0; double tmp = 0.0; for (j = 0; j < n; ++j) { if (i&(1<<j))//检查0到n中存在于i状态的点 { ct++; tmp += p[j]; } } //鸽巢定理 if (ct&1) ans += 1.0/tmp; else ans -= 1.0/tmp; } printf("%.4lf\n",ans); } return 0; }
1007 hdu 4337 http://acm.hdu.edu.cn/showproblem.php?pid=4337
虎哥在最后的几分钟内,写了个爆搜竟然过了,orz虎哥啊。。。我也用爆搜写过的。。。
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #define maxn 155 using namespace std; int map[maxn][maxn],res[maxn]; bool vt[maxn]; int n,m; bool flag; void dfs(int pos,int num) { int i; if (flag) return ; res[num] = pos; for (i = 1; i <= n; ++i) { if (map[pos][i]) { //printf(">>%d\n",i); if (!vt[i]) { //printf("***%d\n",i); vt[i] = true; dfs(i,num + 1); vt[i] = false; } else if (num + 1 == n + 1 && i == 1) { flag = true; break; } } if (flag) return ; } } int main() { //freopen("d.txt","r",stdin); int i,x,y; while (~scanf("%d%d",&n,&m)) { flag = false; memset(map,0,sizeof(map)); memset(vt,false,sizeof(vt)); for (i = 0; i < m; ++i) { scanf("%d%d",&x,&y); map[x][y] = map[y][x] = 1; } vt[1] = true; dfs(1,1); if (flag) { for (i = 1; i < n; ++i) printf("%d ",res[i]); printf("%d\n",res[i]); } else { printf("no solution\n"); } } return 0; }
1009 hdu 4339 http://acm.hdu.edu.cn/showproblem.php?pid=4339
一看就知道是线段树的题目,本来以为自己线段树还可以,于是就很有信心的做了敲出来了一看题意看错了,我求了成两个字符串形同的连续一样的字符串了的个数了。
赛后敲了一下,终于A了,其实处理一下就是一个很简单的单点更新的题目;郁闷啊。。。
不同的点标为1相同的标为0询问时只要求出该店左边不同的点个数,然后再求下一个不同点的位置即可。
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #define maxn 1000007 using namespace std; int val[4*maxn]; char s1[maxn],s2[maxn]; void pushup(int rt) { val[rt] = val[rt<<1] + val[rt<<1|1]; } void build(int l,int r,int rt) { if (l == r) { if (s1[l] == s2[l]) val[rt] = 0;//相同为0,不同为1 else val[rt] = 1; return ; } int m = (l + r)>>1; build(l,m,rt<<1); build(m + 1,r,rt<<1|1); pushup(rt); } void update(int pos,char *sc,int l,int r,int rt,int mk) { if (l == r) { if (mk == 1) s1[l] = sc[0]; if (mk == 2) s2[l] = sc[0]; if (s1[l] == s2[l]) val[rt] = 0; else val[rt] = 1; return ; } int m = (l + r)>>1; if (pos <= m) update(pos,sc,l,m,rt<<1,mk); else update(pos,sc,m + 1,r,rt<<1|1,mk); pushup(rt); } int getsum(int L,int R,int l,int r,int rt) { if (l >= L && r <= R) { return val[rt]; } int res = 0; int m = (l + r)>>1; if (L <= m) res += getsum(L,R,l,m,rt<<1); if (R > m) res += getsum(L,R,m + 1,r,rt<<1|1); return res; } int query(int pos,int l,int r,int rt) { if (l == r) return l; int res = 0; int m = (l + r)>>1; if (pos <= val[rt<<1]) res = query(pos,l,m,rt<<1); else { pos -= val[rt<<1]; res = query(pos,m + 1,r,rt<<1|1); } return res; } int main() { int t; int x,y; char z[3]; int cas = 1; scanf("%d",&t); while (t--) { scanf("%s%s",s1,s2); int len1 = strlen(s1); int len2 = strlen(s2); int len = max(len1,len2); build(0,len,1); int q,op; scanf("%d",&q); printf("Case %d:\n",cas++); while (q--) { scanf("%d",&op); if (op == 2) { scanf("%d",&x); if (s1[x] != s2[x]) printf("0\n"); else { int ret = getsum(0,x,0,len,1);//首先找出[0,x]不同的点的个数 ret++;//++后就是要查找的第几个不同的点了 int pos = query(ret,0,len,1);//返回第ret个不同点的位置 printf("%d\n",pos - x); } } else { scanf("%d%d%s",&x,&y,z); update(y,z,0,len,1,x); } } } return 0; }