1. 程式人生 > >ICPC 2018 Malaysia(solve10/10)

ICPC 2018 Malaysia(solve10/10)

題目連結:upc oj

A題

題意:給你n個人數到第m個人出來,問你最後一個是誰。

思想:約瑟夫環數學問題(公式)

B題

題意:給你n個城市m條路,然後給你k條路徑,必須從u到v,然後問你走完這k條路最小花費是多少,如果不能走通輸出-1

思想:先將k個點和其他k-1個點連線其他,這樣就是一個最大18個點組成的圖。利用狀態壓縮去求解即可。

考慮下當前集合中已經有的點去連線集合中沒有的點,需要建立一條邊是連線上

dp[i][j]代表那個u到另外的v的距離 。D[i][j]代表當前i這個集合中都在了 而且終點是j這個地方的最小的那個花費

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
struct node{
    int to;
    int next;
    ll valu;
}no[4*maxn];
int head[maxn];
int vis[maxn];
ll dist[maxn];
int cnt,n;
struct Node{
    int u;
    int v;
}No[20];
void add(int u,int v,ll valu)
{
    no[cnt].to=v;
    no[cnt].valu=valu;
    no[cnt].next=head[u];
    head[u]=cnt++;
}
void spfa(int s){  
    for(int i=0;i<=n;i++)
    {
        vis[i]=0;
        dist[i]=1e18;
    }
    queue<int> Q;  
    Q.push(s);  
    vis[s]=1;  
    dist[s]=0;  
    while (!Q.empty()){  
        int u=Q.front();  
        Q.pop();  
        vis[u]=0;  
        for (int i=head[u];i!=-1;i=no[i].next){  
            int v=no[i].to;  
            if(dist[v]>dist[u]+no[i].valu){  
                dist[v]=dist[u]+no[i].valu;  
                if (!vis[v]){  
                    vis[v]=1;  
                    Q.push(v);  
                }  
            }  
        }  
    }  
}  
ll dp[20][20];//記錄邊和邊的最短距離 
ll D[270000][20];//狀態壓縮 
int main()
{
    cnt=0;
    memset(head,-1,sizeof(head)); 
    int m,k,a,b;
    ll c;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=1;i<=k;i++)
        scanf("%d%d",&No[i].u,&No[i].v);
          
    for(int i=0;i<20;i++)
        for(int j=0;j<20;j++)
            dp[i][j]=1e18; 
              
    for(int i=1;i<=k;i++)
    {
        spfa(No[i].u);
        for(int j=1;j<=k;j++)
            dp[i][j]=dist[No[j].v];
    }
      
    for(int i=0;i<(1<<k);i++)
        for(int j=0;j<=k;j++) 
            D[i][j]=1e18;
      
    for(int i=0;i< k ;i++)//初始狀態都是本身的開頭到本身的結尾 
        D[1<<i][i] = dp[i+1][i+1];
      
    ll ans=0x3f3f3f3f;
    for(int i=1;i<(1<<k);i++)//列舉所有的狀態 
    {
        for(int j=0;j<k;j++)
        {
            if(!(i&(1<<j)))//需要考慮是當前狀態的點 
                continue;
            for(int kk=0;kk<k;kk++)
            {
                if(i&(1<<kk))//需要是不存在的狀態 
                    continue; 
                D[i|(1<<kk)][kk] = min(D[i|(1<<kk)][kk] , D[i][j]+dp[j+1][kk+1]+dp[kk+1][kk+1]); 
            }
            if(i+1 == (1<<k))
                ans=min(ans,D[i][j]); 
        }
    } 
    if(ans==0x3f3f3f3f)
        printf("-1\n");
    else
        printf("%lld\n",ans);
    return 0;
}

C題

題意:給你一個n,然後問你有多少序列保證一個集合2個數學不會小於等於1,然後起點從1或者2開始。

思想:dfs打表看規律。

D題

題意:給你n個點,m條邊,問你n個點聯通最小花費

思想:最小生成樹

E題

題意:給你2個矩陣問你是否可以乘。

思想:模擬

F題

題意:給你n*n的格子可以分成多少個矩形。

思想:手寫幾項就發現了規律。數太大所以java

G題

題意:給你3種肉,每種肉1kg可以製作多少沙爹,然後給你每樣買的數量和米飯的數量。問你最大利潤

思想:模擬

H題

題意:沒聽懂 隊友過的  

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005;
int a[maxn];
int mp[5][5];
int main()
{
    int T;
    scanf("%d", &T);
    for (int i = 1; i <= T; i++) {
        memset(mp, 0, sizeof(mp));
        int n = 0;
        while (1){
            scanf("%d", &a[n]);
            if (a[n] == 0)
                break;
            int i = a[n]/10, j = a[n] % 10;
            mp[i][j]++;
            n++;
        }
        int flag = 1;
        for (int i = 2; i < n; i++){
            if (a[i] % 10 == a[i - 1] % 10 && a[i] % 10 == a[i - 2] % 10){
                flag = 0;
                break;
            } else if(a[i] / 10 == a[i - 1] / 10 && a[i] / 10 == a[i - 2] / 10) {
                flag = 0;
                break;
            }
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        for(int j = 1; j <= 4; j++){
            int sum = 0;
            for(int i = 1; i <= 4; i++){
                if(i != j)
                    sum += mp[i][j];
            }
            if(mp[j][j] != sum){
                flag = 0;
                break;
            }
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        for(int i = 1; i <= 3; i++){
            for(int j = i + 1; j <= 4; j++){
                if(mp[i][j] != mp[j][i]){
                    flag = 0;
                    break;
                }
            }
            if(flag == 0)
                break;
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        printf("Case #%d: Stroop\n", i);
 
 
    }
    return 0;
}
 

I題

題意:給你n個工廠,每個工廠都有可以製造的型別和回收的型別和將貨物轉給其他工廠的花費。給你一個序列問你從外到內生產和從內到外的回收費用的和的最小值是多少。當全部製造完成之後會收到的時候可以從任意工廠回收。

思想:動態規劃。DP[i][j]代表第i個工廠生產第j件物品的最小花費 。回收一樣  最後求一個和即可

#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
typedef long long ll;
int a[maxn][maxn];//轉運花費 
int b[maxn][maxn];//生產花費 
int c[maxn][maxn];//回收話費
int d[maxn];//n層 
ll dp[maxn][maxn];
int main()
{
	int n,m,q;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
		for(int j=1;j<=m;j++)
			scanf("%d",&b[i][j]);
		for(int j=1;j<=m;j++)
			scanf("%d",&c[i][j]);
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
		scanf("%d",&d[i]);
	for(int i=0;i<=503;i++)
		for(int j=0;j<=503;j++)
			dp[i][j]=1ll<<60;
	for(int i=0;i<=n;i++)//最後一個 
		dp[i][q+1]=0;//需要將0也初始化,因為第一個物品是不需要轉運的費用		
	for(int i=q;i>=1;i--)
	{
		int x=d[i];//當前需要生產的 
		int y=d[i+1];//上一個在哪裡生產的
		for(int j=1;j<=n;j++)
		{
			if(b[j][x]==-1)
				continue;
			for(int k=0;k<=n;k++)
			{
				if(b[k][y]==-1)
					continue;
				dp[j][i]=min(dp[j][i],dp[k][i+1]+b[j][x]+a[k][j]); 
			}
		}
	}	
	long long ans1=1ll<<60;
	for(int i=1;i<=n;i++)
		ans1=min(ans1,dp[i][1]);
	for(int i=0;i<=503;i++)
		for(int j=0;j<=503;j++)
			dp[i][j]=1ll<<60;
	for(int i=0;i<=n;i++)//反過來 
		dp[i][0]=0;//同轉運一樣
		
	for(int i=1;i<=q;i++)
	{
		int x=d[i];//當前需要生產的 
		int y=d[i-1];//上一個在哪裡生產的
		for(int j=1;j<=n;j++)
		{
			if(c[j][x]==-1)
				continue;
			for(int k=0;k<=n;k++)
			{
				if(c[k][y]==-1)
					continue;
				dp[j][i]=min(dp[j][i],dp[k][i-1]+c[j][x]+a[k][j]); 
			}
		}
	}	
	long long ans2=1ll<<60; 
	for(int i=1;i<=n;i++)
		ans2=min(ans2,dp[i][q]);
	printf("%lld\n",ans1+ans2);
	return 0;
} 

J題

題意:給你一個時間和感染的人數,讓你求一個時間的感染人數。

思想:從已知的條件推出來遞增式。