HIT Training Camp IV 题解

89 篇文章 0 订阅

难度分析:本次题目难度呈中间低,两头高的趋势(恶搞一下,无视之吧)

没有数据结构题目,图论题目有两道,一个是网络流,一个算是补充大家图论盲点的题目吧(弦图的判定问题)


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






















  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值