20170924模拟赛

个人卫生综合征

每天BBS都要从家里经过城市中的一段路到学校刷五三。城市中一共有n个路口和m条双向道路,每条双向道路都连接着两个路口ai、bi且有一定的时间花费vi。BBS家编号为1,学校编号为n。今天,BBS由于个人卫生综合征导致他很迟才离开家,他想用膜法改变k条道路的长度使通过其的时间花费vi变为0。现在他问你改变道路长度之后他到学校的最小时间花费是多少?

 

输入格式:

第一行为三个整数n、m、k,接下来的m行每行三个整数ai,bi,vi,分别表示这条路连着的两个路口和通过其所用的时间。

输出格式:

一个整数,表示BBS到学校的最小时间花费。

 

样例输入

样例输出

4 4 1
1 2 10
2 4 10
1 3 1
3 4 100

1

 

样例解释:

更新3->4的道路,最短路线为1->3->4,用时为1+0=1。

 

数据范围:

对于100%的数据:1<=n<=10000,1<=m<=50000,1<=k<=20,1<=vi<=1000000。

 

分层图建最短路——是最短路算法中的一种。

理解了最短路,分层图也好挺好理解的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
inline int read(){
    int num=0,t=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=(num<<1)+(num<<3)+c-'0';c=getchar();}
    return num*t;
}
const int N=10010,INF=0x7fffffff;
struct edge{int t,c;};
struct P{
    int a,b,c;
    bool operator <(const P &hh)const{return c>hh.c;}
};
vector<edge> g[N];
int d[N][30],n,m,k;
void dij(int s){
    priority_queue<P> q;
    memset(d,127,sizeof(d));
    d[s][0]=0;q.push((P){s,0,0});
    while(!q.empty()){
        P p=q.top();q.pop();
        int x=p.a,f=p.b,dis=p.c;
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i].t,c=g[x][i].c;
            if(f<k&&d[y][f+1]>dis){
                d[y][f+1]=dis;
                q.push((P){y,f+1,d[y][f+1]});
            }
            if(d[y][f]>dis+c){
                d[y][f]=dis+c;
                q.push((P){y,f,d[y][f]});
            }
        }
    }
}
int main()
{
    freopen("school.in","r",stdin);
    freopen("school.out","w",stdout);
    n=read();m=read();k=read();
    for(int i=1;i<=m;i++){
        int a=read(),b=read(),c=read();
        g[a].push_back((edge){b,c});
        g[b].push_back((edge){a,c});
    }
    dij(1);
    int ans=INF;
    for(int i=0;i<=k;i++)
        if(d[n][i]<ans)ans=d[n][i];
    printf("%d\n",ans);
    return 0;
}
View Code

你的四边形已如风中残烛

    LGL有一根长为n的木板。现在他想要把它砍成四段长度为整数的木板来做一个四边形,请问他有多少种不同的砍法?注意:四段长度为1、1、2、1和四段长度为1、2、1、1算两种砍法。

 

输入格式:

第一行为一个整数 n,表示木板的长度。

输出格式:

一个整数,不同的砍法数量。

 

样例输入

样例输出

6

6

 

样例解释:

    1122,1212,1221,2112,2121,2211。

数据范围:

对于100%的数据:1<=n<=2500。

 

这题做法倒有挺多的,可以用通项公式推(真的难推),也可以DP(最容易的做法),甚至可以打表(打表打法好)。

这里考虑用DP的做法。dp[i][j]表示长度为i时分成j段的方案数,随便转移。

#include<cstdio>
int n,half,f[2510][5];
int main()
{
    freopen("quad.in","r",stdin);
    freopen("quad.out","w",stdout);
    scanf("%d",&n);f[0][0]=1;
    for(int i=1;i<=n;i++)for(int j=1;j<=4;j++){
        int x=i<(n-1)/2?i:(n-1)/2;
        for(int k=1;k<=x;k++)f[i][j]+=f[i-k][j-1];
    }
    printf("%d\n",f[n][4]);
    return 0;
}
View Code

生命不息刷题不止

 YYH有n道题要做。但是由于他上课做某些事,导致他一题都不会做,只好请LGL代打。LGL为了买自己心爱的坦克,他做第i题要收两笔钱:一笔在YYH叫他做题当天收,另外一笔在叫他做题的第二天收。YYH每天结束的时候都会把剩下的所有钱花光,然后再从父亲LRB处得到m元零花钱用来请LGL做题(也就是说,第一天的时候YYH是没有钱请LGL做题的,每一天用来请LGL做题所用的钱都是前一天LRB给的)。而且,YYH做的题目难度是循序渐进的:就算强如LGL,在做第i题之前也要先把第1到i-1题全部做完。请问YYH将所有题目做完并且把所有钱都付给LGL的最小天数。

 

输入格式:

第一行为两个整数m、n,接下来的n行每一行都有两个数ai和bi,分别表示LGL做第i题所收的两笔钱。

输出格式:

一个整数,表示最小天数。

 

样例输入

样例输出

100 5
40 20
60 20
30 50
30 50
40 40

6

 

样例解释:

            第二天做1、2两题,第三天做3、4两题,第五天做5。在第六天的时候所有钱都付完。

 

数据范围:

对于100%的数据:1<=n<=300,1<=ai、bi<=m<=1000。

 

这题也是DP题。

dp[i][j]表示前i天做了j题后该天可能剩下的最多的钱的数量。

那么dp[i][j]可以转移到dp[i][j+1]、dp[i][j+2]……只要满足剩下的钱不少于0并且下个月的钱够还即可。

同时可以算出dp[i+1][j+1],dp[i+1][j+2]……最后输出满足dp[i][m]存在值的最小i。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
    int t=1,num=0;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
    return num*t;
}
int f[610][310],a[310],b[310];
int main()
{
    freopen("solve.in","r",stdin);
    freopen("solve.out","w",stdout);
    int m,n;scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
    memset(f,-1,sizeof(f));f[2][0]=m;
    int i;
    for(i=2;f[i][n]==-1;i++){
        for(int j=n;j>=0;j--)
            if(f[i][j]!=-1){
            int k=j+1,t1=f[i][j],t2=f[i+1][j]=m;
            while(t1>=a[k]&&t2>=b[k]){
                t1-=a[k];t2-=b[k];
                f[i][k]=max(f[i][k],t1);
                f[i+1][k]=max(f[i+1][k],t2);
                k++;
            }
        }
    }
    printf("%d\n",i);
    return 0;
}
View Code

    给出一张有n个点和m条双向边的图,要求求出1到n的次短路的长度。一条边可以多次通过。

 

输入格式:

第一行为两个整数n和m。接下来的m行每行三个整数ai,bi,vi,分别表示这条路连着的两个点和他的长度。

 

输出格式:

一个整数,表示次短路的长度。

 

样例输入

样例输出

4 4
1 2 100
2 4 200
2 3 250
3 4 100

450

 

样例解释:

最短:1->2->4。

次短:1->2->3->4。

 

数据范围:

对于 100%的数据:1<=n、vi<=5000,1<=m<=100000。

 

这题显然就是裸的次短路啊。用dijstral解决。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
    int num=0,t=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+c-'0';c=getchar();}
    return num*t;
}
const int maxn=5005,INF=1e9;
int n,m,d[maxn],d2[maxn];
struct edge{int to,c;};vector<edge>g[maxn];
typedef pair<int,int>P;
void add(int f,int t,int c){
    g[f].push_back((edge){t,c});
    g[t].push_back((edge){f,c});
}
void solve(){
    priority_queue< P,vector<P>,greater<P> >q;
    fill(d,d+n,INF);fill(d2,d2+n,INF);
    q.push(P(0,0));d[0]=0;
    while(!q.empty()) {
        P p=q.top();q.pop();
        int v=p.second,dis=p.first;
        if(d2[v]<dis)continue;
        for(int i=0;i<g[v].size();i++){
            edge e=g[v][i];int dis2=dis+e.c;
            if(d[e.to]>dis2)
                swap(d[e.to],dis2),q.push(P(d[e.to],e.to));
            if(d2[e.to]>dis2&&d[e.to]<dis2)
                d2[e.to]=dis2,q.push(P(d2[e.to],e.to));
        }
    }
    printf("%d\n",d2[n-1]);
}
int main(){
    freopen("short.in","r",stdin);
    freopen("short.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int a=read(),b=read(),c=read();add(a-1,b-1,c);
    }solve();
    return 0;
}
View Code

本文由Yzyet编写,网址为www.cnblogs.com/Yzyet。非Yzyet同意,禁止转载,侵权者必究。

转载于:https://www.cnblogs.com/Yzyet/p/7642686.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值