noip2017 Day1

第一题数学公式推导,反正推完后就是(a*b)-a+b,没什么好说的,送分。

第二题懒得放题目,总而言之就是一道大模拟,判断循环,还挺麻烦的。。,但只要仔细都能打对。

主要是在每个循环判断能不能进,如果不能进就得停止累加时间复杂度,后面的E结束要对应前面的F,我是用了一个stack+结构体来判断这次能不能进循环,会不会停止,和更新答案了否(就是cifang有没有比之前+1),然后后面E结束循环时从栈顶开始减,就可以准确判断了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
struct one
{
	int x,st;
	char c;
};
int pre(string x)
{
	if(x[2] == '1') return 0;
	else
	{
		int now = 4,num = 0;
		while(isdigit(x[now]))
		{
			num = num * 10 + (x[now] - '0');
			if(num > 50) return -1;
			now++;
		}
		return num;
	}
}
int pre2(string x)
{
		int now = 0,num = 0;
		while(isdigit(x[now]))
		{
			num = num * 10 + (x[now] - '0');
			now++;
		}
		return num;
}
int main()
{
//	freopen("complexity.in","r",stdin);
//	freopen("complexity.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> t;
	while(t--)
	{
		int l,cifang = 0,Maxc = 0,cnt = 0; 
		bool wa = 0,err = 0; string str;
		int stop = 0;
		cin >> l;
		set<char> s; stack<one> w;
		cin >> str;
		int base = pre(str);
		if(base == -1) wa = 1;
		for(int i = 1;i <= l;i++)
		{
			char a,b; string c,d; int nc,nd;
			cin >> a;
			if(a == 'F') 
			{
				cnt++;
				cin >> b >> c >> d; 
				if(err) continue;
				if(s.count(b))
					err = 1;
				else s.insert(b);
				int sto = stop,ci = cifang;
				one tmp;
				tmp.st = 0;
				if(stop <= 0)
					if(isdigit(c[0]) && !isdigit(d[0])) cifang++;
				if(isdigit(c[0]) && isdigit(d[0]))
				{
					int nc = pre2(c),nd = pre2(d);
					if(nc > nd) stop++;
				} 
				else if(!isdigit(c[0]) && isdigit(d[0])) stop++;
				tmp.c = b,tmp.x = 0;
				if(sto != stop) tmp.st = 1;
				if(cifang > ci) tmp.x = 1;
				Maxc = max(Maxc,cifang);
				w.push(tmp);
			}
			else {
				cnt--;
				if(cnt < 0)
					err = 1;
				if(err) continue;
				one tmp = w.top();
				w.pop();
				cifang -= tmp.x;stop -= tmp.st; 
				s.erase(tmp.c);
			} 
		}
		if(cnt || err) cout << "ERR" << endl;
		else
			if(wa || (Maxc != base)) cout << "No" << endl;
			else cout << "Yes" << endl;
	}
	return 0;	
} 

第三题就比较难了,又改了将近一下午orz。

这道题运用的思想和最短路计数的思想差不多,但是题目当中还允许不超过最短路长度K的路径,那么就需要在最短路计数上稍作变通才可以:

  1. 当然和最短路计数的套路一样,我们需要知道最短路到底是多少才可以判断某条路径是否满足要求,就先用SPFA算法来求一下最短路。
  2. 知道最短路之后我们怎么求解满足路径的条数呢?假想一下我们现在从起点开始走——由于我们已经使用SPFA大法来完成了求最短路的使命,我们自然而然的就维护出来了一个d数组(d[i]表示i点到起点的最短距离)这种功能就可以告诉我们当前所走的路是否有“冤枉路”了。解释:“冤枉路”是指从A点至B点所走的路径不是最短路,也就是说一共允许你走K冤枉路的长度,每走一次冤枉路都会消耗这个余额。
  3. 通过想象这个过程就能知道前面你的选择会对后面有影响,很自然的想到dp求解,dp(i,j)就表示你当前在i点,还允许你走j容量的“冤枉路”。
  4. 那什么时候有无穷多条合法路线呢?首先肯定是在路径里出现了环。(解释:因为简单路线肯定是有限条,无穷的肯定是有环)题目里提到了0边,这就使我们进一步想到0环——如果这个环不是零环,那肯定就不是在最短路上面了(何必绕着一点跑到黑呢!)肯定就会消耗“冤枉路”容量,就不可能是有限条。所以如果有0环出现,判断-1即可。

一些注意事项

  • 虽然1号点一定能走到n号点,但是公园中有些点可能无法到达N,所以通过建立反向图的方式反向SPFA来排除那些无法到终点的点,把能到终点的点alive[i]值标为1即可,在dp的时候把alive值为0的点跳过即可
  • 本题有多组数据,请大家一定要记得清空数组!
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int MAXN = 1e5 + 5;
    const int MAXM = 2e5 + 5;
    const int INF = 0x7fffffff;
    int p;
    int T,n,m,k;
    int ans = 0;
    int head[MAXM*2],rhead[MAXM*2],cnt = 0,rc = 0;
    int dis[MAXN],rdis[MAXN];
    int dp[MAXN][55];
    bool vis[MAXN][60],alive[MAXN];
    
    void read(int &x)
    {
        int f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    
    struct edge
    {
    	int next,to,w;
    }e[MAXM*2],r[MAXM*2];
    
    void add(int u,int v,int w)
    {
    	e[++cnt].next = head[u];
    	e[cnt].to = v;
    	e[cnt].w = w;
    	head[u] = cnt;	
    } 
    void radd(int u,int v,int w)
    {
    	r[++rc].next = rhead[u];
    	r[rc].to = v;
    	r[rc].w = w;
    	rhead[u] = rc;	
    } 
    
    int dfs(int a,int b)
    {
    	if(b < 0) return 0;
    	if(vis[a][b] == 1) return -INF;
    	if(dp[a][b] != -1) return dp[a][b];
    	vis[a][b] = 1;
    	int cnt = 0;
    	if(a == n) cnt++;
    	for(int i = head[a];i;i = e[i].next)
    	{
    		int to = e[i].to;
    		int w = e[i].w;
    		int u = dis[to] - dis[a];
    		if(alive[to] == 0) continue;
    		int wgt = dfs(to,b-(w-u));
    		if(wgt == -INF) return -INF;
    		cnt = (cnt + wgt) % p; 
    	}
    	dp[a][b] = cnt % p;
    	vis[a][b] = 0;
    	return cnt;
    }
    
    int q[MAXM*4],h,t;
    
    void spfa()
    {
    	memset(q,0,sizeof q);
    	h = t = 0;
    	q[++t] = 1;
    	dis[1] = 0;
    	while(h != t)
    	{
    		int x = q[++h];
    		for(int i = head[x];i;i = e[i].next)
    		{
    			int to = e[i].to,w = e[i].w;
    			if(dis[x] + w < dis[to])
    			{
    				dis[to] = dis[x] + w;
    				q[++t] = to;
    			}
    		} 
    	}
    }
    
    void rspfa()
    {
    	memset(q,0,sizeof q);
    	h = t = 0;
    	q[++t] = n;
    	alive[n] = 1;
    	while(h != t)
    	{
    		int x = q[++h];
    		for(int i = rhead[x];i;i = r[i].next)
    		{
    			int to = r[i].to;
    			if(alive[to] == 0)
    			{
    				alive[to] = 1;
    				q[++t] = to;
    			}
    		} 
    	}
    }
    
    
    int main()
    {
    	read(T);
    	while(T--)
    	{
    		cnt = 0;
    		rc = 0;
    		read(n);read(m);read(k);read(p);
    		for(int i = 1;i <= n;i++)
    		{
    			head[i] = 0;rhead[i] = 0;
    			alive[i] = 0;
    			for(int l = 0;l <= k;l++)
    			{
    				dp[i][l] = -1;
    				vis[i][l] = 0;
    			}
    		}
    		for(int i = 1;i <= m;i++)
    		{
    			int x,y,w;
    			read(x);read(y);read(w);
    			add(x,y,w);
    			radd(y,x,w);
    		}
    //		memset(dis,0x3f,sizeof dis);
    		for(int i = 2;i <= n;i++) dis[i] = INF;
    		spfa();
    		rspfa(); 
    		int ans = dfs(1,k);
    		if(ans == -INF) printf("%d\n",-1);
    		else printf("%d\n",ans);
    	}
    	return 0;	
    } 
    

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/82814110
今日推荐