暑假tes05

测验都是模板题;

A题:http://poj.org/problem?id=1258

题意:输入n,接下来有N*N的矩阵,代表从每个点到其他点的路径长度;要求每个点都要走到,求最短的长度是多少;明显的最小生成树模板题;

ps:prime算法:从源点开始,从集合中选取到其他点最短的点,再加入集合,然后更新距离,更新点;

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stdio.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=105;
int vis[N];
int dis[N];
int ma[N][N];
int n,ans;
void init(){//初始化 
	ans=0;
	for(int i=1;i<=n;i++){
		vis[i]=0;
		for(int j=1;j<=n;j++){
			ma[i][j]=inf;
		}
	}
}
void prime(){
	for(int i=1;i<=n;i++)dis[i]=ma[1][i];
	dis[1]=0;           //从点1出发, 
	vis[1]=1;           //将点1加入集合 
	
	for(int v=1;v<n;v++){  //将剩下的n-1个点加入集合,则代表所有点都被选取(也就是都能经过)的最短距离 
		int minn=inf;
		int flag=0;
		for(int i=1;i<=n;i++){
			if(!vis[i]&&dis[i]<minn){
				minn=dis[i];
				flag=i;
			}
		}
		vis[flag]=1;    //遍历所有点,找到从集合中到未加入集合中的最短边的点; 
		ans=ans+dis[flag];   //加上距离 
		for(int i=1;i<=n;i++){          //更新 
			if(!vis[i]&&dis[i]>ma[flag][i]){
				dis[i]=ma[flag][i];
			}
		}
	}
	
}
int main(){
	while(scanf("%d",&n)!=EOF){
		init();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)
			cin>>ma[i][j];
		}
		prime();
		printf("%d\n",ans);
	}
	return 0;
}

 Kruskal算法:找边,至始至终找最短的边,判断是否成环,即可,成环判断(用并查集)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 105;
struct Edge{
    int u,v;
    int w;
}edge[N*N];
int p[N],n,ans=0,m;
int maze[N][N];
void init(){
    ans=0,m=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++)
            maze[i][j]=inf;
    }
}
int Find(int x){   //并查集压缩路径 
    return x == p[x] ? x : p[x] = Find(p[x]);
}
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
void kruskal(){
    sort(edge,edge+m,cmp);  //将边的长短排序; 

    for(int i=0;i<=n;i++)p[i]=i; //初始化各个集合,将各个集合的爸爸设为自己,也就是一个人一个集合; 

    for(int i=0;i<m;i++){   //遍历所有的边, 
        int u=Find(edge[i].u);
        int v=Find(edge[i].v);
        if(u!=v){    //当不是一个集合时,代表不成环,将其加入 
            p[u]=v;        //实际上可以用一个变量记录加入边的数量,如果 数量等于n-1则代表已经遍历所有的点 
            ans+=edge[i].w; 
           
        }
         //例如  if(num==n-1)break; 
    }

}
int main(){
    while(cin>>n){
        init();
        //ans=0,m=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
                cin>>maze[i][j];
        }

        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                edge[m].u=i;
                edge[m].v=j;
                edge[m++].w=maze[i][j];
            }
        }
        kruskal();
        cout<<ans<<endl;
    }
    return 0;
}

 B题:http://poj.org/problem?id=3468

线段树模板题,题意:Q查询,C更改都是区间;

虽说是模板题,还是讲讲,懒标记:该标记的意思是如果有区间要更改值,我就先只更改自己,若某个操作要查询或者更改到自己的儿子,我就将懒标记传递下去,传递过程:则是自己区间长短*每个人要更改的值,因为区间更改是每个人要更改同样的数字,即(r-l+1)*f(f为懒标记也就是存的c 操作最后一个数的值);

#include <iostream>
using namespace std;
typedef long long int ll;
const ll N = 1e6+10;
struct Tree{    //l代表左边界,r为右边界,f为懒标记; 
    ll l,r,f;
    ll w;   //w为该区间的值; 
}tree[N*4];
ll ans=0;
void down(ll node){
    tree[node*2].f+=tree[node].f;
    tree[node*2].w+=(tree[node*2].r-tree[node*2].l+1)*tree[node].f;
    tree[node*2+1].f+=tree[node].f;
    tree[node*2+1].w+=(tree[node*2+1].r-tree[node*2+1].l+1)*tree[node].f;
    tree[node].f=0;
}

void build(ll node,ll a,ll b){
    tree[node].l=a,tree[node].r=b;
    if(tree[node].l==tree[node].r){//当递归到叶子时,输入值 
        cin>>tree[node].w;
        return ;
    }
    ll m=(a+b)/2;
    build(node*2,a,m);
    build(node*2+1,m+1,b);
    tree[node].w=tree[node*2].w+tree[node*2+1].w;//否则爸爸的值等于两个儿子的值相加 
}
void querry(ll node,ll a,ll b){  //查询操作 
    if(tree[node].l>=a&&tree[node].r<=b){ //如果查询的区间大于 该区间,例如我查询2-5,该节点表示3-4,当然可以直接累加上 
        ans+=tree[node].w;
        return ;
    }
    if(tree[node].f)down(node);  //如果有懒标记,则下传 
    ll m=(tree[node].l+tree[node].r)/2;

    if(a<=m)querry(node*2,a,b);
    if(b>m)querry(node*2+1,a,b);
}
void add(ll node,ll a,ll b,ll v){     //区间修改 
    if(tree[node].l>=a&&tree[node].r<=b){
        tree[node].w+=(tree[node].r-tree[node].l+1)*v;    
        tree[node].f+=v;   //标记上懒标记 
        return ;
    }
    if(tree[node].f)down(node);  
    ll m=(tree[node].l+tree[node].r)/2;
    if(a<=m)add(node*2,a,b,v);
    if(b>m)add(node*2+1,a,b,v);
    tree[node].w=tree[node*2].w+tree[node*2+1].w;
}

int main()
{
    ll n,q;
    cin>>n>>q;
    build(1,1,n); //建树 
//    for(ll i=1;i<=15;i++)
//    cout << tree[i].w<< endl;

    while(q--){
        ans=0;
        char type;
        cin>>type;
        if(type=='Q'){
            ll x,y;
            cin>>x>>y;
            querry(1,x,y);
            printf("%I64d\n",ans);
        }
        else{
            ll x,y,z;
            cin>>x>>y>>z;
            add(1,x,y,z);
        }
    }

    return 0;
}

C题:http://poj.org/problem?id=1064

有n根缆线,分成K段,使每段最长,求长为多少;

ps:利用二分;

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stdio.h>
typedef long long int ll;
using namespace std;

const int N = 100010;
ll a[N];
int n,k;
bool binary(ll t){
	ll res=0;
	for(int i=0;i<n;i++){
		res+=(ll)(a[i]/t);
	}
	if(res>=k)return true;
	else
	return false;
}
int main(){
	
	while(~scanf("%d%d",&n,&k)){
		ll maxx=-999;
		for(int i=0;i<n;i++){
			double qq;
			cin>>qq;
			a[i]=qq*100.0; //划为整数 
			maxx=max(maxx,a[i]);
		}
		
		ll l=1,r=maxx,lon=0;
		
		//int number=100;
		while(l<=r){
			ll mid=(l+r)/2;
			if(binary(mid)){
				lon=max(lon,mid);
				l=mid+1;
			}
			else
			r=mid-1;
		}
		printf("%.2f\n",double(lon/100.0));
		//cout<<(lon/100.0)<<endl;
	}
	return 0;
}

D题:http://poj.org/problem?id=3061

给你一个数,代表有几个测试样例,接下来n,k,代表n个数,连续元素相加大于k的最小序列长度;

可用前缀和二分找长度,也可以用尺取法,(尺取法O(n)好点)

#include <iostream>
using namespace std;
const int N = 1e5+10;
int a[N];
int main()
{
    int _;
    cin>>_;
    while(_--){
        int left=1,right=1,sum=0; //left代表 左边界,right右边界; 
        int n,s;
        cin>>n>>s;
        for(int i=1;i<=n;i++)cin>>a[i];
        
        int ans=n+1; //区间长度 
        sum=a[1];
        while(right<n){
            if(sum<s&&ans>(right-left+1)){   //如果接下来的区间长度已经大于ans了,就没必要再扩展了,直接去掉左区间的值即可 
                right++;
                sum+=a[right];
            }
            else{
                ans=min(ans,right-left+1);
                sum-=a[left];
                left++;
            }
        }
        if(ans<=n)
        cout << ans << endl;
        else
            cout<<'0'<<endl;
    }
    return 0;
}

G题:http://poj.org/problem?id=3255

次短路径:在堆优化的dijkstra上加一个 次短路dis2;

#include <iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N = 100010;
#define inf 0x3f3f3f3f
struct Edge{
    int to;
    int cost;
    Edge(int tt=0,int cc=0):to(tt),cost(cc){}
};
int dis[5005],dis1[5005];
typedef pair<int,int> p;
int n,r;
vector<Edge>edge[N];
void dijkstra(){
    fill(dis,dis+n+1,inf);//将dis,dis1初始为inf 
    fill(dis1,dis1+n+1,inf);
    dis[1]=0;
    priority_queue<p,vector<p>,greater<p> > q;
    q.push(p(0,1));
    while(!q.empty()){
        p now=q.top();
        q.pop();
        int v=now.second,d=now.first;
        if(d>dis1[v])continue;

        for(int i=0;i<edge[v].size();i++){
            Edge e=edge[v][i];
            int d2=e.cost+d;
            
            if(dis[e.to]>d2){
                swap(dis[e.to],d2);
                q.push(p(dis[e.to],e.to));
            }

            if(d2>dis[e.to]&&d2<dis1[e.to]){//更新次短路径  
                dis1[e.to]=d2;
                q.push(p(dis1[e.to],e.to));
            }
        }
    }
    cout<<dis1[n]<<endl;
}
int main()
{

    cin>>n>>r;
    for(int i=1;i<=r;i++){
        int u,v,w;
        cin>>u>>v>>w;
        edge[u].push_back(Edge(v,w));
        edge[v].push_back(Edge(u,w));
    }
    dijkstra();
    //cout << "Hello world!" << endl;
    return 0;
}

H题:http://acm.hdu.edu.cn/showproblem.php?pid=1237

简单计算器;

#include<iostream>
#include<stack>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<iomanip>
#include<string>
using namespace std;
int main(){
    char op;
    double num,temp;
    while(scanf("%lf",&num)){
        stack<double>sum;
        op=getchar();
        if(num==0&&op=='\n')break;
        sum.push(num);
        while(1){
            scanf("%c %lf",&op,&num);

            switch(op){
                case '+':sum.push(num);break;
                case '-':sum.push(-num);break;
                case '*':{temp=sum.top();sum.pop();sum.push(temp*num);break;}
                case '/':{temp=sum.top();sum.pop();sum.push(temp/num);break;}
            }
            if(getchar() == '\n')break;
        }
        double ans=0;
        while(!sum.empty()){
            ans+=sum.top();
            sum.pop();
        }
        printf("%.2f\n",ans);
    }
    return 0;
}

H题:http://acm.hdu.edu.cn/showproblem.php?pid=1896

题意:丢石子,奇数丢,偶数不丢,求最后最远的石子离刚开始的石子多远;

ps:利用优先队列,先按距离排序,距离相同的丢得近的先 出队,利用temp 每次 !temp,则代表 奇偶;

#include <iostream>
#include<queue>
using namespace std;
struct stone{
    int pi;
    int di;
    bool operator < (const stone &u) const {
        if(pi==u.pi){
            return di>u.di;
        }
        return pi>u.pi;
    }
};
int main()
{
    int _;
    cin>>_;
    while(_--){
        stone p;
        priority_queue<stone> q;
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>p.pi>>p.di;
            q.push(p);
        }
        bool temp=true;
        int ans=0;
        
        while(!q.empty()){
            stone e;
            e=q.top();
            q.pop();
            if(temp){
                e.pi+=e.di;  //距离 加上丢的距离,再入队 
                q.push(e);
            }
            ans=e.pi;
            temp=!temp;  //变奇偶 
        }
        cout << ans<< endl;
    }

    return 0;
}
发布了97 篇原创文章 · 获赞 3 · 访问量 9439

猜你喜欢

转载自blog.csdn.net/foolishpichao/article/details/99560100
tes
05