1. 程式人生 > >【loli的胡策】高一信心場11.2(bitset+dp+二分+樹形揹包)

【loli的胡策】高一信心場11.2(bitset+dp+二分+樹形揹包)

1、比賽(contest)

N(1 <= N <= 100)個同學按1..N依次編號參加羽毛球比賽。每人的實力不盡相同,不存在兩個人實力相同的情況。
比賽分成若干輪進行,每一輪是兩個人對決。如果編號為A的同學的實力強於編號為B的同學(1 <= A <= N; 1 <= B <= N; A != B),那麼她們的對決中,編號為A的同學總是能勝出。
你為了知道實力的具體排名,於是你找來M(1 <= M <= 4,500)輪比賽的結果,希望能根據這些資訊,推斷出儘可能多的同學得實力排名。比賽結果保證不會自相矛盾。

輸入格式:

第1行:用空格隔開的2個整數:N 和 M
第2..M+1行: 每行2個整數A、B(用空格隔開的),描述一輪比賽,每行的第一個編號為勝者

輸入樣例:

5 5
4 3
4 2
3 2
1 2
2 5

輸出格式:

1個整數,表示排名可確定的同學的數目

輸出樣例:

2

輸出說明:

編號為2的同學的排名為第4,編號為5的同學排名最後。其他3人的排名仍無法確定。

題解:

啊這個題一眼秒?top原圖+逆圖一波然後bitset亂搞?

程式碼:

#include <queue>
#include <bitset>
#include <cstdio>
#include <cstring>
#define N 105
#define M 4505
using namespace std; int n,m,i; bitset<105>in[N],out[N];bool vis[N]; int tot,nxt[M],point[N],v[M],tot1,nxt1[M],point1[N],v1[M],du[N],du1[N]; void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;} void addline1(int x,int y){++tot1; nxt1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y;} void
top() { int i; queue<int>q; for (i=1;i<=n;i++) { if (!du[i]) q.push(i); in[i][i]=1; } while (!q.empty()) { int now=q.front(); q.pop(); vis[now]=1; for (int i=point[now];i;i=nxt[i]) { in[v[i]]|=in[now]; du[v[i]]--; if (!du[v[i]] && !vis[v[i]]) vis[v[i]]=1,q.push(v[i]); } } memset(vis,0,sizeof(vis)); for (i=1;i<=n;i++) { if (!du1[i]) q.push(i); out[i][i]=1; } while (!q.empty()) { int now=q.front(); q.pop(); vis[now]=1; for (int i=point1[now];i;i=nxt1[i]) { out[v1[i]]|=out[now]; du1[v1[i]]--; if (!du1[v1[i]] && !vis[v1[i]]) vis[v1[i]]=1,q.push(v1[i]); } } } int main() { freopen("contest.in","r",stdin); freopen("contest.out","w",stdout); scanf("%d%d",&n,&m); for (i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); addline(x,y);du[y]++; addline1(y,x);du1[x]++; } top(); int ans=0; for (i=1;i<=n;i++) if (in[i].count()+out[i].count()-1==n) ans++; printf("%d",ans); }

2、運動計劃(run)

地鼠誤入一個隧道它必須在N(1 <= N <= 10,000)分鐘內離開,否則隧道就會被人注滿水,在每分鐘的開始,地鼠會選擇下一分鐘是用來爬行還是休息。 地鼠的體力限制了她爬行的距離。更具體地,如果地鼠選擇在第i分鐘內爬行,她可以在這一分鐘內爬可k_i(1 <= k_i <= 1,000)米,並且她的絕望指數會增加1。不過,無論何時地鼠的絕望指數都不能超過M(1 <= M <= 500)。如果地鼠選擇休息,那麼她的絕望指數就會每分鐘減少1,但她必須休息到絕望指數恢復到0為止。在絕望指數為0時休息的話,絕望指數不會再變動。逃離開始時,地鼠的絕望指數為0。還有,在N分鐘的逃離結束時,地鼠的絕望指數也必須恢復到0,否則她將對自己的一生失去信心。請你計算一下,地鼠最多能爬多少米。(已知地鼠爬行距離達到最大時恰好能逃離隧道。

輸入格式:

第1行: 2個整數:N 和 M(用空格隔開)
第2。。N+1行: 第i+1行為1個整數:k_i

輸入樣例 (run.in):

5 2
5
3
4
2
10

輸出格式:

輸出一個整數,表示地鼠能爬行的最大距離

輸出樣例 (run.out):

9

輸出說明:

第1分鐘內爬行,在第2分鐘內休息,在第3分鐘內爬行,剩餘時間用來休息。

題解:

以前好像做過這道題
第一次寫掛了20pts?被學弟踩
儘量避免讓狀態向未來轉移啊!
dp的狀態很好設計:dp[i][j]表示時間i絕望值為j的最遠距離

程式碼:

#include <cstdio>
#include <iostream>
using namespace std;
int f[10005][505],k[10005];
int main()
{
    freopen("run.in","r",stdin);
    freopen("run.out","w",stdout);
    int n,m,i,j,l;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%d",&k[i]);
    for (i=1;i<=n;i++)
    {
        f[i][0]=max(f[i][0],f[i-1][0]);
        for (j=m;j>=1;j--)
        {
          f[i][j]=max(f[i][j],f[i-1][j-1]+k[i]);
          if (i>j) f[i][0]=max(f[i][0],f[i-j][j]);
        } 
    }
    printf("%d",f[n][0]);
}

3、找路徑(road)

圖上有N(1 <= N <= 1,000)個點,一共m(1 <= m<= 10,000)條邊。 第i條邊的端點為s_i、t_i,長度為L_i (1 <= L_i <= 1,000,000)。資料中保證每對{s_i,t_i}最多隻出現1次。你的任務是找一條將點1和點N連起來的路徑,如果存在不超過k(0=< k<=n)條邊的路徑則輸出0。否則輸出第k+1大的邊的最小值.

輸入格式:

第1行: 3個整數:N m K(用空格隔開)
第2..m+1行: 第i+1行為3個整數:s_i t_i L_i(用空格隔開)

輸入樣例

5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6

輸出格式:

1個整數

輸出樣例

4

輸出說明:

路徑為:1->3->2->5

題解:

這個一開始以為是原題,只不過那個題讓求最小距離,這個讓求最大值的最小值
熟悉不?二分!
可以轉化為最短路,比mid長的長度設為1,其他設為0,如果最短路小於等於k,說明這個值ok啊
題目沒說雙向邊差評!題目不給不連通-1差評!

程式碼:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1005
#define M 30005
#define INF 1e9
using namespace std;
struct hh{int maxx,k;};
int tot,nxt[M],point[N],v[M],c[M],dis[N],k,n,m;
bool vis[N],vv=0;
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
} 
void spfa()
{
    queue<int>q;
    q.push(1);
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[1]=0;
    while (!q.empty())
    {
        int now=q.front();q.pop();
        for (int i=point[now];i;i=nxt[i])
          if (dis[v[i]]>dis[now]+1)
          {
            dis[v[i]]=dis[now]+1;
            if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
          }
    }
    if (dis[n]>INF) printf("-1"),vv=1;
    else if (dis[n]<=k) printf("0"),vv=1;
}
bool check(int mid)
{
    queue<int>q;
    q.push(1);
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[1]=0;
    while (!q.empty())
    {
        int now=q.front();q.pop();vis[now]=0;
        for (int i=point[now];i;i=nxt[i])
          if (c[i]<=mid) 
          {
            if (dis[v[i]]>dis[now]) 
            {
                dis[v[i]]=dis[now];
                if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
            }
          }
          else 
            if (dis[v[i]]>dis[now]+1)
            {
                dis[v[i]]=dis[now]+1;
                if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
            }
    }
    if (dis[n]>k) return 0;
    return 1;
}
int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    int l=INF,r=0;
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addline(x,y,z);r=max(r,z);l=min(l,z);
    }
    spfa();
    if (vv) return 0;
    int ans=0;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;  
    }
    printf("%d",ans);
}

4、道路(roads)

有一棵樹,有m(1到150)個節點,如果砍掉幾條邊這棵樹就會變成多棵樹,求最少砍掉幾條邊可以在得到的樹中至少有一顆節點個數為n(1到m)。

輸入說明:

第1行 m n , 第2到第m行,每行兩個整數a 和b,表示a是b的父結點(1號點為根節點)。

輸出說明:

一個數為最少砍掉的邊數。

輸入樣例:

11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11

輸出樣例:

2

樣例說明:

砍掉1—4和1—5。

題解:

好像是原題
f[i][j]表示以i為根節點分離出來j個點的最少切割數量

程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200
using namespace std;
const int INF=1e9;
int tot,nxt[N*2],point[N],v[N*2],size[N],f[N][N],n,ans,m,du[N];
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void treedp(int x,int fa)
{
    f[x][1]=du[x];
    for (int i=point[x];i;i=nxt[i]) 
      if (v[i]!=fa)
      {
        treedp(v[i],x);
        for (int j=m;j>=1;j--)
          for (int k=1;k<=j;k++)
            f[x][j]=min(f[x][j],f[v[i]][k]+f[x][j-k]-2);
      }
    ans=min(ans,f[x][m]);
}
int main()
{
    freopen("roads.in","r",stdin);
    freopen("roads.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addline(x,y);du[x]++;du[y]++;
    }
    memset(f,0x3f,sizeof(f));
    ans=INF;
    treedp(1,0);
    printf("%d",ans);
}