2015-2016 下半學期 第三週 訓練

1、AOJ2249

題意:求最短路的同時要求最小花費。

題解:spfa模板加個數組鬆弛花費。

代碼:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e8

using namespace std;

struct node{
    int w,c,nxt,to;
}edge[2005000];
int head[10050],cnt;
int cost[10050],dis[10050];
bool vis[10050];

void add(int u,int v,int w,int c){
    edge[cnt].to=v; edge[cnt].c=c;
    edge[cnt].w=w;  edge[cnt].nxt=head[u];
    head[u]=cnt++;
    edge[cnt].to=u; edge[cnt].c=c;
    edge[cnt].w=w;  edge[cnt].nxt=head[v];
    head[v]=cnt++;
}
queue<int> q;
void spfa(int s,int n){
    while (!q.empty()) q.pop();
    for (int i=1;i<=n;i++)
        cost[i]=dis[i]=inf;
    memset(vis,0,sizeof(vis));
    dis[s]=0,vis[s]=1;cost[s]=0;
    q.push(s);
    while (!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt){
            int to=edge[i].to;
            int val=edge[i].w;
            if (dis[to]>dis[u]+val || (dis[to]==dis[u]+val && cost[to]>edge[i].c)){
                dis[to]=dis[u]+val;
                cost[to]=edge[i].c;
                if (!vis[to]){
                    q.push(to);
                    vis[to]=1;
                }
            }
        }
    }
}
int main(){
    int n,m,u,v,w,c;
    while (~scanf("%d%d",&n,&m)){
        if (n==0 && m==0) break;
        memset(head,-1,sizeof(head));
        cnt=0;
        for (int i=1;i<=m;i++){
            scanf("%d%d%d%d",&u,&v,&c,&w);
            add(u,v,c,w);
        }
        spfa(1,n);
        int ans=0;
        for (int i=1;i<=n;i++)
            ans+=cost[i];
        printf("%d\n",ans);
    }
    return 0;
}

2、uva11090

題意:給定一個n個點m條邊的加權有向圖,求平均權值最小的迴路。

題解:二分平均值mid,然後邊權-mid,spfa判圖裏有沒有負環。 

代碼:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-6
#define inf 1e8

using namespace std;

struct node{
    int to,nxt;
    db w;
}e[10010];
int head[110],n,m,cnt=0;
void add(int u,int v,db w){
    e[cnt].to=v; e[cnt].w=w;
    e[cnt].nxt=head[u]; head[u]=cnt++;
}
db d[110];
int num[110];
bool vis[110];
bool spfa(db mid){
    int v,i;
    queue<int> q;
    memset(vis,0,sizeof(vis));
    memset(num,0,sizeof(num));
    for (i=1;i<=n;i++) d[i]=inf;
    for (i=1;i<=n;i++){
        q.push(i);
    }
    while (!q.empty()){
        int u=q.front();
        vis[u]=0;
        q.pop();
        for (i=head[u];i!=-1;i=e[i].nxt){
            v=e[i].to;
            if (d[v]>d[u]+e[i].w-mid){
                d[v]=d[u]+e[i].w-mid;
                if (!vis[v]){
                    num[v]++;
                    vis[v]=1;
                    if (num[v]>=n) return 0;
                    q.push(v);
                }
            }
        }
    }
    return 1;
}
int main(){
    int u,v,t,cas=1;
    db ans=inf;
    scanf("%d",&t);
    while (t--){
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt=0;
        db ans=inf;
        db l=0,r=0,mid;
        for (int i=1;i<=m;i++){
            int u,v;
            db w;
            scanf("%d%d%lf",&u,&v,&w);
            add(u,v,w);
            if(u==v) ans=min(ans,w);
            r=max(r,w);
        }
        printf("Case #%d: ", cas++);
        if(spfa(r+10000)){
            puts("No cycle found.");
            continue;
        }
        while(r-l>1e-3){
            mid=(r+l)/2.0;
            if(!spfa(mid)){
                ans=min(ans,mid);
                r=mid;
            }
            else l=mid;
        }
        printf("%.2f\n",ans);
    }
    return 0;
}

3、uva11478

題意:給定一個有向圖,每條邊都有一個權值,每次你可以選擇一個結點v和整數d,把所有以v爲終點的邊權值減少d,把所有以v爲起點的邊權值增加d,最後要讓所有的邊權值非負且最大。

題解:ai-aj<w 多個這種形式的不等式可以構成一個差分約束系統,轉化在最短路上即j到i連一條權值爲w的邊,求最短路。在此題中,對於每個節點u,可以計算sum[u]=多次操作下u點的權值變化。因此每條邊a->b都可以列出一個不等式sum(b)-sum(a)<=w(a,b)-x。 二分x。

代碼:

#include <cstdio>
#include <deque>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 510;
struct arc {
    int to,w,next;
    arc(int x = 0,int y = 0,int z = -1) {
        to = x;
        w = y;
        next = z;
    }
} e[500000];
int head[maxn],tot,n,m;
void add(int u,int v,int w) {
    e[tot] = arc(v,w,head[u]);
    head[u] = tot++;
}
int d[maxn],cnt[maxn];
bool in[maxn];
bool spfa(int x) {
    deque<int>q;
    for(int i = 1; i <= n; ++i) {
        cnt[i] = 1;
        d[i] = 0;
        in[i] = true;
        q.push_back(i);
    }
    while(!q.empty()) {
        int  u = q.front();
        q.pop_front();
        in[u] = false;
        for(int i = head[u]; ~i; i = e[i].next) {
            int tmp = e[i].w - x;
            if(d[e[i].to] > d[u] + tmp) {
                d[e[i].to] = d[u] + tmp;
                if(!in[e[i].to]) {
                    if(++cnt[e[i].to] > n) return false;
                    in[e[i].to] = true;
                    if(!q.empty() && d[q.front()] > d[e[i].to])
                        q.push_front(e[i].to);
                    else q.push_back(e[i].to);
                }
            }
        }
    }
    return true;
}
int main() {
    int u,v,w;
    while(~scanf("%d%d",&n,&m)) {
        memset(head,-1,sizeof head);
        int low = 1,high = 0;
        for(int i = tot = 0; i < m; ++i) {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            high = max(high,w);
        }
        if(!spfa(1)) puts("No Solution");
        else if(spfa(high+1)) puts("Infinite");
        else {
            int ret;
            while(low <= high) {
                int mid = (low + high)>>1;
                if(spfa(mid)) {
                    ret = mid;
                    low = mid+1;
                } else high = mid - 1;
            }
            printf("%d\n",ret);
        }
    }
    return 0;
}

4、hdu4786

題意:一堆黑邊,一堆白邊,問能不能組成一棵生成樹,樹上白邊條數是斐波那契數。

題解:因爲代表邊種類的數只能是0/1,所以把這個東西當作權值,把邊排個序,看一棵生成樹最少用到多少條白邊,最多用到多少條白邊,如果這兩個數之間有素數,即代表可以生成,因爲黑白邊之間可以相互替代,因爲黑白邊的數量最大值都分別算出來了。

代碼:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-6
#define inf 1e8

using namespace std;

int pri[1000010];
struct Edge{
    int u,v,w;
}e[200010];
int f[200010];
int find(int x){
    if (f[x]==-1) return x;
    return f[x]=find(f[x]);
}
bool cmp1(Edge a,Edge b){
    return a.w<b.w;
}
bool cmp2(Edge a,Edge b){
    return a.w>b.w;
}
int main(){
    int tot=1;
    pri[0]=1; pri[1]=2;
    while (pri[tot]<=1000010){
        pri[tot+1]=pri[tot]+pri[tot-1];
        tot++;
    }
    int T,cas=1;
    scanf("%d",&T);
    while (T--){
        int n,m;
        scanf("%d%d",&n,&m);
        for (int i=0;i<m;i++)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        sort(e,e+m,cmp1);
        memset(f,-1,sizeof(f));
        int cnt=0;
        for (int i=0;i<m;i++){
            int x=find(e[i].u);
            int y=find(e[i].v);
            if (x!=y){
                f[x]=y;
                if (e[i].w==1) cnt++;
            }
        }
        int low=cnt;
        memset(f,-1,sizeof(f));
        sort(e,e+m,cmp2);
        cnt=0;
        for(int i=0;i<m;i++){
            int x=find(e[i].u);
            int y=find(e[i].v);
            if(x!=y){
                f[x]=y;
                if(e[i].w==1) cnt++;
            }
        }
        int high=cnt;
        bool flag=1;
        for(int i=1;i<=n;i++)
            if(find(i)!=find(1)){
                flag=0;
                break;
            }
        if (!flag){
            printf("Case #%d: No\n",cas++);
            continue;
        }
        bool ff=0;
        for (int i=0;i<=tot;i++)
            if (pri[i]>=low && pri[i]<=high)
                ff=1;
        if (ff) printf("Case #%d: Yes\n",cas++);
        else printf("Case #%d: No\n",cas++);

    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章