寒假实录 chapter 4

Intervals

题意:给出N个区间(a_{i},b_{i},c_{i}),求一个最短的序列使得序列包含了区间 [a_{i},b_{i}] 里的至少c_{i} 个数。输出最短的长度。

定义d[x] 为前x个数里包含的数的个数,那么根据题意有 d[b_{i}]-d[a_{i}]>=c_{i} ,此外 d[j]-d[j-1]<=1 即 d[j-1]-d[j]>=-1 还有 d[j]-d[j-1]>=0。 这样就可以通过差分约束跑spfa求最长路得出答案了。

#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <cstring>
#include <set>
#define ll long long
#define lld unsigned long long
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;
const int MAXN = 51111;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const int maxn=100010;

struct Edge {
	int from, to, dist;
};
struct SPFA {
	int n, m;
	vector <Edge> edges;
	vector <int> G[MAXN];
	bool vis[MAXN];
	int d[MAXN], p[MAXN];
	int cnt[MAXN];

	void init(int n) {
		this -> n = n;
		for (int i = 0; i < n; i++) G[i].clear();
		edges.clear();
	}

	void addedge(int from, int to, int dist) {
		edges.push_back((Edge) {from, to, dist});
		m = edges.size();
		G[from].push_back(m - 1);
	}

	void solve(int l,int r) {
		queue <int> q;
	    memset (vis, false, sizeof vis);
	    memset (cnt, 0, sizeof(cnt));
	    for (int i = l; i <= r; i++)
        {
            d[i]=0;vis[i]=true;
            q.push(i);
        }
	    while (!q.empty ()) {
	        int u = q.front (); q.pop ();
	        vis[u] = false;
	        for (int i = 0; i < (int)G[u].size(); i++) {
	            Edge& e = edges[G[u][i]];
	            int v = e.to, w = e.dist;
	            if (d[v] < d[u] + w) {
	                d[v] = d[u] + w;
	                if (!vis[v]) {
	                    vis[v] = true; q.push (v);
	                }
	            }
	        }
	    }
	}

} spfa;

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int l=50000;
        int r=0;
        spfa.init(50001);
        for(int i=1; i<=n; i++)
        {
            int u,v,c;
            scanf("%d %d %d",&u,&v,&c);
            spfa.addedge(u-1,v,c);
            l=min(l,u-1);
            r=max(r,v);
        }
        for(int i=l+1; i<=r; i++)
        {
            spfa.addedge(i-1,i,0);
            spfa.addedge(i,i-1,-1);
        }
        spfa.solve(l,r);
        printf("%d\n",spfa.d[r]);
    }
    return 0;
}

Cashier Employment

题意转化:一条长度为24的线段上有24个点,给出N个起点和长度为8的线,求最少的线使得每个点都能被覆盖R(i)次。

定义d[i] 为1~i里有多少起点。则有关系d[i]-d[i-1]>=0d[i]-d[i-1]<=num[i]d[i]-d[i-8]>=R[i](i>=8)d[24]+d[i]-d[i+16]>=R[i] 即d[i]-d[i+16]>=R[i]+d[24](i<8)。其中num[i]为在i时刻实际最多能有的起点。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <cstring>
#include <set>
#define ll long long
#define lld unsigned long long
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;
const int MAXN = 51111;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const int maxn=100010;

struct Edge {
	int from, to, dist;
};
struct SPFA {
	int n, m;
	vector <Edge> edges;
	vector <int> G[MAXN];
	bool vis[MAXN];
	int d[MAXN], p[MAXN];
	int cnt[MAXN];

	void init(int n) {
		this -> n = n;
		for (int i = 0; i < n; i++) G[i].clear();
		edges.clear();
	}

	void addedge(int from, int to, int dist) {
		edges.push_back((Edge) {from, to, dist});
		m = edges.size();
		G[from].push_back(m - 1);
	}

	bool solve(int s) {
		queue <int> q;
	    memset (vis, false, sizeof vis);
	    memset (cnt, 0, sizeof(cnt));
	    fill(d,d+25,-inf);
	    d[s]=0;
	    q.push(s);
	    while (!q.empty ()) {
	        int u = q.front (); q.pop ();
	        vis[u] = false;
	        for (int i = 0; i < (int)G[u].size(); i++) {
	            Edge& e = edges[G[u][i]];
	            int v = e.to, w = e.dist;
	            if (d[v] < d[u] + w) {
	                d[v] = d[u] + w;
	                if (!vis[v]) {
	                    vis[v] = true; q.push (v);
	                    if(++cnt[v]>25) return false;
	                }
	            }
	        }
	    }
	    return true;
	}

} spfa;
int R[100];
int num[100];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(num,0,sizeof num);
        for(int i=1;i<=24;i++) scanf("%d",&R[i]);
        int N;
        scanf("%d",&N);
        for(int i=1;i<=N;i++)
        {
            int x;scanf("%d",&x);
            num[x+1]++;
        }
        bool flag=0;
        for(int j=0; j<=N; j++)
        {
            spfa.init(100);
            for(int i=1; i<=24; i++)
            {
                spfa.addedge(i-1,i,0);
                spfa.addedge(i,i-1,-num[i]);
                if(i>=8)
                {
                    spfa.addedge(i-8,i,R[i]);
                }
                else
                {
                    spfa.addedge(16+i,i,R[i]-j);
                }
            }
            spfa.addedge(0,24,j);
            if(spfa.solve(0)&&spfa.d[24]==j)
            {
                printf("%d\n",j);
                flag=1;break;
            }
        }
        if(flag==0) printf("No Solution\n");
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值