难度分析:本次题目难度呈中间低,两头高的趋势(恶搞一下,无视之吧)
没有数据结构题目,图论题目有两道,一个是网络流,一个算是补充大家图论盲点的题目吧(弦图的判定问题)
1. zoj1015 Fishing Net
题解见: http://blog.csdn.net/yang_7_46/article/details/8181302
2. zoj 1008 Gnome Tetravex
裸搜索题目,不带优化搜索会超时。减枝:方格的类型可能相同,因此判断方格类型,然后记录个数再搜索就可以了。
#include <cstdio> #include <cstring> using namespace std; struct node { int u, l, r, d; int c; } b[30]; int n, m, kind; int indx[6][6]; bool ok; bool can(int x, int y, int k) { if (y > 0) { if (b[k].l != b[indx[x][y-1]].r) return false; } if (x > 0) { if (b[k].u != b[indx[x-1][y]].d) return false; } return true; } void dfs(int depth) { if (depth == n) { ok = true; return ; } if (ok) return ; int x = depth/m, y = depth%m; for (int i=0; i<kind; i++) if (b[i].c && can(x, y, i)) { indx[x][y] = i; b[i].c--; dfs(depth+1); b[i].c++; } } int main() { int cas = 0; while (scanf("%d", &m) == 1 && m) { n = m * m; memset(indx, 0, sizeof(indx)); int u, r, l, d; kind = 0; for (int i=0; i<n; i++) b[i].c = 0; for (int i=0; i<n; i++) { scanf("%d%d%d%d", &u, &r, &d, &l); bool flag = false; for (int j=0; j<kind; j++) if (b[j].u==u && b[j].r==r && b[j].d==d && b[j].l==l) { b[j].c++; flag = true; break; } if (!flag) { b[kind].u = u; b[kind].r = r; b[kind].d = d; b[kind].l = l; b[kind++].c = 1; } } ok = false; dfs(0); if (cas) printf("\n"); if (ok) printf("Game %d: Possible\n", ++cas); else printf("Game %d: Impossible\n", ++cas); } return 0; }
解法大家都听烂了的题目,枚举角度
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; int main() { double h, l1, r1, l2, r2; double a[220]; int n; while (scanf("%d", &n)==1 && n) { scanf("%lf%lf%lf%lf%lf", &h, &l1, &r1, &l2, &r2); double angle, x, tmp; for (int i=0; i<n; i++) scanf("%lf", &a[i]); h *= 19.6; int ans = 0, enemy; for (int j=1; j<=1000; j++) { angle = acos(-1)*j/1000; enemy = 0; bool flag = true; for (int i=0; i<n; i++) { tmp = a[i]*cos(angle); x = (tmp+sqrt(tmp*tmp+h))*a[i]*sin(angle)/9.8; if (l1 <= x && x <= r1) enemy++; if (l2 <= x && x <= r2) { flag = false; break; } } if (flag && enemy > ans) ans = enemy; } printf("%d\n", ans); } return 0; }
4. hdu4455 substrings(杭州赛区C题)
动态规划,感觉需要出点dp题目,然后就找了一个,先放这里,贴个别人的代码。
用DP的思路O(n)复杂度解决。
以样例为例说明:
1 1 2 3 4 4 5;
明显dp[1]=n=7;
长度为1的时候有7个区间。从长度为1到长度为2,就是把前6个区间往后增加一个数,把最后一个区间去掉。
增加的6个数要看在该区间是否出现过,只要看它上一个相等的元素距离是否大于2
所以dp[2]=dp[1]-1+4;
以此类推就可以得出所以的dp值了。
dp[i]=dp[i-1]-A+B;
减的A是最后一个长度为i-1的区间的不同数的个数,这个很容易预处理得出来。
加的B是第t个数到它上一个数的距离大于i-1的个数.
这个B值也容易得出。
用s[i]表示离上一个数的距离为i的个数,不断减掉就得到B了。
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> using namespace std; const int MAXN=1000010; int a[MAXN];//1-n输入的数列 int f[MAXN];//f[i]表示a[i]在前面最近出现的位置,f[i]==0表示从左到右第一次出现 int s[MAXN];//s[i]表示 t-f[t]==i,1<=t<=n的t的个数,即离上一个相等元素的距离为i的个数 long long dp[MAXN];//需要输出的结果 int ss[MAXN];//ss[i]表示最后的i个数含有的不同元素的个数 int main() { int n; int m; while(scanf("%d",&n)==1 && n) { memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); //顺着求s数组 for(int i=1;i<=n;i++) { scanf("%d",&a[i]); s[i-f[a[i]]]++; f[a[i]]=i; } memset(f,0,sizeof(f));//f数组标记在后面是否出现过 ss[1]=1; f[a[n]]=1; for(int i=2;i<=n;i++) { if(f[a[n-i+1]]==0) { f[a[n-i+1]]=1; ss[i]=ss[i-1]+1; } else ss[i]=ss[i-1]; } dp[1]=n; int sum=n; //从dp[i-1]扩展到dp[i]就是去掉最后一个区间的个数,把前面的区间长度增加1, //加上相应增加的种类数 for(int i=2;i<=n;i++) { dp[i]=dp[i-1]-ss[i-1];//减掉最后一个区间的种类数 sum-=s[i-1]; dp[i]+=sum;//加上前面的区间增加一个长度后增加的种类数 } scanf("%d",&m); int t; while(m--) { scanf("%d",&t); printf("%I64d\n",dp[t]); } } return 0; }
5. poj 1149 pigs
最大流,但是此题的构图比较复杂,具体构图方法在网上搜吧。暑期集训的网络流资料里面有汇总。
最大流模板是沙漠学长的,带了注释了的。。汗。。。。。
#include <cstring> #include <algorithm> #include <vector> #include <cstdio> #define SETZR(a) memset(a,0,sizeof(a)) using namespace std; //定义常量:边数、点数和无穷 const int MAXM = 10900; const int MAXN = 105; const int INF = 0x7f7f7f7f; //边的结构体 //此模板中图以池子法存储 struct record { int v, f, next; } edge[MAXM]; int pointer[MAXN], dis[MAXN], vh[MAXN], cl; int his[MAXN], di[MAXN], pre[MAXN]; void connect(int a, int b, int f) { cl++; edge[cl].next = pointer[a]; edge[cl].v = b; edge[cl].f = f; pointer[a] = cl; cl++; edge[cl].next = pointer[b]; edge[cl].v = a; edge[cl].f = 0; //若为无向边,则f = f pointer[b] = cl; } int maxflow(int s, int t, int n) { //最大流过程 vh[0] = n; //初始化GAP数组(默认所有点的距离标号均为0,则距离标号为0的点数量为n) for (int i = 0; i < n; i++) di[i] = pointer[i]; //初始化当前弧 int i = s, aug = INF, flow = 0; //初始化一些变量,flow为全局流量,aug为当前增广路的流量 bool flag = 0; //标记变量,记录是否找到了一条增广路(若没有找到则修正距离标号) while (dis[s] < n) { his[i] = aug; //保存当前流量 flag = 0; int p = di[i]; while (p != 0) { if ((edge[p].f > 0) && (dis[edge[p].v] + 1 == dis[i])) {//利用距离标号判定可行弧 flag = 1; //发现可行弧 di[i] = p; //更新当前弧 aug = min(aug, edge[p].f); //更新当前流量 pre[edge[p].v] = p; //记录前驱结点 i = edge[p].v; //在弧上向前滑动 if (i == t) {//遇到汇点,发现可增广路 flow += aug; //更新全局流量 while (i != s) {//减少增广路上相应弧的容量,并增加其反向边容量 edge[pre[i]].f -= aug; edge[pre[i]^1].f += aug; i = edge[pre[i]^1].v; } aug = INF; } break; } p = edge[p].next; } if (flag) continue; //若发现可行弧则继续,否则更新标号 int min = n - 1; p = pointer[i]; while (p != 0) { if ((edge[p].f > 0) && (dis[edge[p].v] < min)) { di[i] = p; //不要忘了重置当前弧 min = dis[edge[p].v]; } p = edge[p].next; } --vh[dis[i]]; if (vh[dis[i]] == 0) break; //更新vh数组,若发现距离断层,则算法结束(GAP优化) dis[i] = min + 1; ++vh[dis[i]]; if (i != s) {//退栈过程 i = edge[pre[i]^1].v; aug = his[i]; } } return flow; } int main() { int n, m, s, t; while (scanf("%d%d", &m, &n) == 2) { //初始化 cl = 1; SETZR(dis); SETZR(vh); SETZR(pointer); //建图 int pig[1005]; vector<int> bian[1005]; for (int i=1; i<=m; i++) scanf("%d", &pig[i]); s = 0, t = n+1; for (int i=1; i<=n; i++) { int k, a; scanf("%d", &k); for (int j=0; j<k; j++) { scanf("%d", &a); bian[a].push_back(i); } scanf("%d", &a); connect(i, t, a); } for (int i=1; i<=m; i++) if (bian[i].size() > 0) { connect(s, bian[i][0], pig[i]); for (unsigned int j=1; j<bian[i].size(); j++) connect(bian[i][j-1], bian[i][j], INF); } printf("%d\n", maxflow(s, t, n+2)); } return 0; }