当前位置: 首页 > news >正文

2016年NK冬季训练赛 民间题解

A题 水题,考察对行的读入和处理,注意使用long long

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int main()
{
  char ch;
  long long a, sum=0, Min;
  char S[200];
  while(cin.getline(S, 100))
  {
        Min = 1e9; a = 0;
        for(int i = 0; i < strlen(S); i++)
        {
          if(S[i] == ' ')
            {
                Min = min(Min, a);
                a = 0;
            } else
                a = a*10 + S[i] - '0';
        }
        if(a != 0) Min = min(Min, a); 
        sum += Min;   
  }
    cout<<sum<<endl;
}

 

B题 贪心算法,先排序,然后依次兑换即可,注意使用long long

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
struct T
{
    int x, y;
    bool operator <(const T &B) const
    { return y < B.y; }
}a[100500];
int main()
{
    long long n, k;
    while(cin>>n>>k)
    {
        for(int i = 0; i < n; i++) cin>>a[i].x>>a[i].y;
        sort(a, a+n);
        int ans = -1;
        for(int i = 0; i < n; i++)
        {
            if(k < a[i].y) break;
            k += a[i].x;
            ans = i;
        }
        cout<<ans+1<<endl;
    }
}

 

C题 可以用dp做,如果把dp的转移方程变成一个矩阵,那么这个矩阵恰好就是这个图的邻接矩阵,然后只要求它的k次幂即可,注意存在自环和重边的情况

  这里可以利用矩阵乘法的一个优化,可以把常数优化很多。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <iomanip>
using namespace std;
const int mod = 999983;
const int maxn = 111;
int n,m;
struct Matrix
{
    long long v[maxn][maxn];
    int n;
    Matrix() { memset(v, 0, sizeof(v));}
    Matrix operator *(const Matrix &B)
    {
        Matrix C; C.n = n;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
            {
                if(v[i][j] == 0) continue; 
                for(int k = 0; k < n; k++)
                    C.v[i][k] = (C.v[i][k] + v[i][j]*B.v[j][k])%mod;
            }
        return C;
    }
}A, B;
Matrix power(Matrix A, int k)
{
    Matrix ans;
    ans.n = n;
    for(int i = 0; i < n; i++) ans.v[i][i] = 1;
    while(k)
    {
        if(k&1) ans = ans*A;
        A = A*A; k >>= 1;
    }
    return ans;
}
int a, b, k, t;
int main()
{
    while(cin>>n>>m>>k>>t)
    {
        A.n = n;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d %d", &a, &b);
            A.v[a-1][b-1]++;
            if(a != b) A.v[b-1][a-1]++;
        }
        B = power(A, k);
        while(t--)
        {
            scanf("%d %d", &a, &b);
            printf("%d\n", B.v[a-1][b-1]);
        }
    }
    return 0;
}

 

D题 建图后直接floyed即可,在树上的边权是距离除以速度v,然后再枚举出在同一x坐标的两个点,边权为自由落体的时间。(这里代码有个bug,更新边权要用min的方法更新)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
double eps = 1e-10;
double d[111][111];
struct point
{
    double x, y;
}p[111];
int n, v, f;
double dis(point &A, point &B)
{ return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y)); }
int main()
{
    while(cin>>n>>v)
    {
    for(int i = 1; i <= n; i++)
        for(int j = 1;  j <= n; j++)
             d[i][j] = 1e8;
    for(int i = 1; i <= n; i++)
    {
        cin>>p[i].x>>p[i].y>>f;
        if(f == 0) continue;
        d[i][f] = dis(p[i], p[f])/v;
        d[f][i] = d[i][f];
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            if(i == j) continue;
            if(p[i].x == p[j].x && (p[i].y - p[j].y > eps)) 
                d[i][j] =  sqrt((p[i].y - p[j].y)*2/10.0);
        }
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(d[i][j] - d[i][k] - d[k][j] > eps) d[i][j] = d[i][k] + d[k][j];
    printf("%.2f\n", d[1][n]);
    }
}

 

E题 贪心算法,先排序,最左侧的必须首先覆盖,然后依次类推,不断覆盖,不难证明这是最优的

#include <iostream>
#include <algorithm>
using namespace std;
int a[10000];
int main()
{
    int L, N, l;
    cin>>L>>N>>l;
    for(int i = 1; i <= N; i++) cin>>a[i];
    sort(a+1, a+1+N);
    int li = -1000, ans = 0;
    for(int i = 1; i <= N; i++)
    {
        if(a[i] - li > l) 
        {
            ans++;
            li = a[i];
        }
    }
    cout<<ans<<endl;
}

 

F题 动态规划,利用滚动数组,f[j]表示交易了j次但并未买入一个股票的状态,g[j]表示交易了j次但买入了股票的状态,然后对每一个股票都需要做一个买或者不买的决策,最后输出max(g[j])即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10050;
long long a[N], f[2][N*2], g[2][N*2];
int main()
{
    long long n, k;
    while(cin>>n>>k)
    {
        k *= 2;
        for(int i = 1; i <= n; i++)    cin>>a[i];
        memset(f, 128, sizeof(f));
        memset(g, 0, sizeof(g));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= k; j++)
            {
                f[i&1][j] = max(g[(i-1)&1][j-1] - a[i], f[i&1][j]);
                g[i&1][j] = max(f[(i-1)&1][j-1] + a[i], g[i&1][j]);
                g[i&1][j] = max(g[(i-1)&1][j], g[i&1][j]);
                f[i&1][j] = max(f[(i-1)&1][j], f[i&1][j]);
            }
        }
        long long ans = 0;
        for(int j = 0; j <= k; j++) ans = max(ans, g[n&1][j]);
        cout<<ans<<endl;
    }
}

 

G题 树型背包动态规划,dp[x][m]表示在x结点用了m个火把向下探索所得到的最大价值,然后转移的时候利用dfs转移即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 111, maxm = 111111;
vector <int> G[maxn];
int dp[maxn][maxm];
struct thing
{ 
    int l, v;
}a[maxn];
int n, M;
 
void dfs(int x, long long m)
{
    for(int i = 0; i < G[x].size(); i++)
    {
        int to = G[x][i];
        for(int j = 0; j <= (m*10 - a[to].l)/10; j++) dp[to][j] = dp[x][j] + a[to].v;
        dfs(to, (m*10 - a[to].l)/10);
        for(int j = 0; j <= (m*10 - a[to].l)/10; j++) 
            dp[x][j+(a[to].l+9)/10] = max(dp[x][j+(a[to].l+9)/10], dp[to][j]);
    }
}
int main()
{
    //freopen("a.txt", "r", stdin);
    while(cin>>M>>n)
    {
        for(int i = 1; i <= n; i++) G[i].clear();
        memset(dp, 0, sizeof(dp));
        int x, y, L, v;
        for(int i = 1; i <= n; i++)
        {
            cin>>x;
            while(x--)
            {
                cin>>y>>L>>v;
                G[i].push_back(y);
                a[y].l = L; a[y].v = v;
            }
        }
        dfs(1, M);
        cout<<dp[1][M]<<endl;
    }
}

 

H题 采药

这个题的背包容量非常大,普通的01背包转移在时间和空间上都无法通过

但由于是随机数据,我们可以利用分块的思想进行优化

初始时先把1~C看成一个大整块

然后第一次更新,用这个大整块去更新出来两个分割的小块

(每一块的值代表从l到r这个容量内的dp值,即dp[l~r]。如果l~r内存在dp值不同的数据,则将这一块分成更多的小块来满足dp值相同的条件)

下一次再用这2个分割的小块去更新出更多的小块

如果仅仅这样做,显然空间和时间上通过也很困难

于是我们每次更新后进行一个维护操作

把值相等且相邻的块合并成一个大块,由于随机数据,每一次维护后,块的数量实际上都非常少(sqrt(n)左右)

于是每次更新的复杂度就是100左右

整体复杂度就是n*sqrt(n)(玄学)

 

实现过程中,有比较多的细节,要多加注意。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
struct Data
{
    LL l, r, v;
    Data() {}
    Data(LL _l, LL _r, LL _v):l(_l), r(_r), v(_v) { }
};
struct thing
{
    LL cost, v;
    bool operator <(const thing &B) const
    { return cost > B.cost; }
}a[1111];
vector <Data> B[3];
map<int, LL> M;
int n; 
LL t;
int main()
{
    while(cin>>n>>t)
    {
        for(int i = 1; i <= n; i++)    cin>>a[i].cost>>a[i].v;
        sort(a+1, a+1+n);
        LL Sum = 0;
        for(int i = n; i >= n-10; i--)  Sum += a[i].v;
        B[0].push_back(Data(0, t, 0));
        for(int i = 1; i <= n; i++)
        {
            B[1].clear(); B[1].push_back(Data(0, min(a[i].cost-1, t), 0));
            for(int j = 0; j < B[0].size(); j++)
            {
                Data& e = B[0][j];
                if(e.l + a[i].cost > t) break;
                B[1].push_back(Data(e.l + a[i].cost, min(t, e.r + a[i].cost), e.v + a[i].v)); 
            }
            for(int j = 0, k = 0; j < B[0].size(); j++)
            {
                if(B[0][j].r >= B[1][k].l)
                    B[2].push_back(Data(max(B[0][j].l, B[1][k].l), min(B[0][j].r, B[1][k].r), max(B[1][k].v, B[0][j].v))); 
                if(B[0][j].r > B[1][k].r)    k++, j--;
            }
            B[0].clear(); M.clear();
            for(int j = 0; j < B[2].size(); j++) M[B[2][j].v] = B[2][j].r;
            for(int j = 0; j < B[2].size(); j++)
            {
                if(M[B[2][j].v])
                {
                    B[0].push_back(Data(B[2][j].l, M[B[2][j].v], B[2][j].v));
                    M[B[2][j].v] = 0;
                }
            }
            B[2].clear();
        }
        cout<<B[0][B[0].size()-1].v<<endl;
        B[0].clear();
    }
}

 

I题 逛公园

十分有趣的一道题目,由于它是一个有向的完全图且不存在环

首先先证明这样一个结论

* 如果这个图有n个点且不存在环,那么必定有一个点,它的入度是0,出度是n-1

证明:反证法,如果不存在这样一个点,那么不妨设一个入度最小的点x,它的入度是m,那么出度就是n-m-1

  考虑所有指向它的m个点中的一个点y,那么可以得到,y也必定指向所有x指向的(n-m-1)个点(因为如果y不全部指向它们,那么肯定会存在环)

  然后y的入度就是n - (n-m+1) = m-1,显然y的入度比x的入度小

  所以矛盾,所以一定存在一个点的入度是0

 

那么接下来把这个点直接删掉(并不会对环的存在有任何影响)  ,图就变成了n-1个点,那么我们同样可以得到必定有一个点入度是0,出度是n-2。

以此类推,我们得到这个图不存在环,那么这n个点的入度分别是0,1,2....n-1

也就是我们需要决策这n个点的入度分别是多少,以使改变的边数最少

然后可以用状态压缩DP来解决

dp[i][s]表示选了i个点,状态为s所需要改变的最小边数

那么再选下一个点来更新dp[i+1][s+(1<<i)]

最后答案就是dp[n][(1<<n) - 1]

 

中间还可以利用位运算优化,把n*n*(2^n)优化为n*(2^n)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
 
int M[20][20];
vector<int> S[20];
int dp[(1<<20)];
int T[(1<<20)], table[20];
int main()
{
    int n;
    for(int i = 0; i < (1<<20); i++)
    {
        int temp = 0;
        for(int j = 0; j < 20; j++) if((1<<j)&i) temp++;
        S[temp].push_back(i); T[i] = temp;
    }
    while(cin>>n)
    {
        memset(table, 0, sizeof(table));
        memset(dp, 126, sizeof(dp));
        memset(M, 0, sizeof(M));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                cin>>M[i][j];
        for(int i = 1; i <= n; i++) M[i][i] = 1;
        for(int i = 1; i <= n; i++)
            for(int j = n; j >= 1; j--)
                table[i] = (table[i]<<1) + M[i][j]^1;
        dp[0] = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                for(int k = 0; k < S[i-1].size(); k++)
                {
                    int s = S[i-1][k];
                    if(1<<(j-1)&s) continue;
                    int temp = 0;
                    dp[s + (1<<(j-1))] = min(dp[s + (1<<(j-1))], dp[s] + T[table[j]&(~s)]);
                }
            }
        }
        cout<<dp[(1<<n)-1]<<endl;
    }
}

 

J题 过河卒,简单动态规划,把马可以控制的点都删除,然后按照f[i][j] = f[i-1][j] + f[i][j-1]转移就可以

#include <iostream>
#include <cstdio>
using namespace std;
 
long long dp[2000][2000];
bool flag[2000][2000];
int dx[4] = {-2, -1, 1, 2};
int dy[4] = {1, 2, 2, 1};
int x[2000], y[2000];
int main()
{
    int xb, yb, N;
    cin>>xb>>yb>>N;
    for(int i = 0; i < N; i++)
        cin>>x[i]>>y[i];
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < N; j++)
        {
            if(x[j] + dx[i] < 0) continue;
            flag[y[j] + dy[i]][x[j] + dx[i]] = 1;
            if(y[j] - dy[i] < 0) continue;
            flag[y[j] - dy[i]][x[j] + dx[i]] = 1;
        }
    }
    for(int j = 0; j < N; j++) flag[y[j]][x[j]] = 1;
    flag[0][0] = 1;
    dp[0][0] = 1;
    for(int j = 0; j <= xb; j++)
        if(!flag[0][j]) dp[0][j] = dp[0][j-1]%((int)1e9+7);
    for(int i = 1; i <= yb; i++)
    {
        if(!flag[i][0]) dp[i][0] = dp[i-1][0];
        for(int j = 1; j <= xb; j++)
            if(!flag[i][j]) dp[i][j] = (dp[i-1][j] + dp[i][j-1])%((int)1e9+7);
    }
    cout<<dp[yb][xb]<<endl;
}

 

转载于:https://www.cnblogs.com/Saurus/p/6189191.html

相关文章:

  • Tips
  • ratina 视网膜屏幕解决方案大全
  • rtmp拉流测试工具
  • cmd中java -jar *.jar 提示Unable to access jarfile *.jar或Windows不能用鼠标双击运行jar文件怎么办解决方案...
  • gulp同步执行任务
  • HBase内置过滤器的一些总结
  • 【VBA编程】09.使用Excle集合对象
  • 树莓派上Java程序作为linux服务并开机自动启动
  • tracert与pathping
  • 线程池及并发编程基础总结
  • Ztree当节点没有下级时不显示下拉图标
  • Bootstrap表单验证插件bootstrapValidator使用方法整理
  • 寻找多数元素问题
  • chattr加锁文件引起yum更新时报错处理
  • Java迭代器
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • ECMAScript6(0):ES6简明参考手册
  • emacs初体验
  • ERLANG 网工修炼笔记 ---- UDP
  • ESLint简单操作
  • flask接收请求并推入栈
  • Java深入 - 深入理解Java集合
  • js中forEach回调同异步问题
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • October CMS - 快速入门 9 Images And Galleries
  • QQ浏览器x5内核的兼容性问题
  • 分类模型——Logistics Regression
  • 理清楚Vue的结构
  • 想使用 MongoDB ,你应该了解这8个方面!
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • #微信小程序:微信小程序常见的配置传旨
  • (06)Hive——正则表达式
  • (3)llvm ir转换过程
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (二)hibernate配置管理
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (南京观海微电子)——COF介绍
  • (转)VC++中ondraw在什么时候调用的
  • **PHP分步表单提交思路(分页表单提交)
  • .form文件_SSM框架文件上传篇
  • .htaccess 强制https 单独排除某个目录
  • .NET BackgroundWorker
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net 后台导出excel ,word
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [Android] Amazon 的 android 音视频开发文档
  • [BUUCTF 2018]Online Tool
  • [bzoj1912]异象石(set)
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [datastore@cyberfear.com].Elbie、[thekeyishere@cock.li].Elbie勒索病毒数据怎么处理|数据解密恢复