难度分析:本次题目难度呈中间低,两头高的趋势(恶搞一下,无视之吧)
没有数据结构题目,图论题目有两道,一个是网络流,一个算是补充大家图论盲点的题目吧(弦图的判定问题)
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;
}
3. hdu 4445 Crazy Tank(金华赛区的D题)
解法大家都听烂了的题目,枚举角度
#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;
}